From 6249a6bf2a3c33702e1e924d6fb9695534ecfa2d Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 1 Aug 2023 23:07:12 -0500 Subject: [PATCH 01/13] start of NXxas reader --- larch/io/__init__.py | 1 + larch/io/nexus_xas.py | 268 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 269 insertions(+) create mode 100644 larch/io/nexus_xas.py diff --git a/larch/io/__init__.py b/larch/io/__init__.py index cb9d53967..073f61174 100644 --- a/larch/io/__init__.py +++ b/larch/io/__init__.py @@ -44,6 +44,7 @@ is_specfile) from .stepscan_file import read_stepscan +from .nexus_xas import NXxasFile def read_tiff(fname, _larch=None, *args, **kws): """read image data from a TIFF file as an array""" diff --git a/larch/io/nexus_xas.py b/larch/io/nexus_xas.py new file mode 100644 index 000000000..bb85ed96d --- /dev/null +++ b/larch/io/nexus_xas.py @@ -0,0 +1,268 @@ +import numpy as np +import h5py +from larch.io import read_xdi +from larch.utils.strutils import bytes2str + +NXXAS_URL = 'https://download.nexusformat.org/doc/html/classes/applications/NXxas.html' + +LATTICE_CONSTANTS = {'Si': 5.4310205, 'C': 3.567095, 'Ge': 5.64613} + +def safe_log(x, extreme=50): + return np.log(np.clip(x, np.e**-extreme, np.e**extreme)) + +def parse_mono_reflection(refl): + refl = refl.replace(',', ' ') + if refl.startswith('(') and refl.startswith(')'): + refl = refl[1:-1] + if len(refl) == 3: + return tuple([int(refl[0]), int(refl[1]), int(refl[2])]) + return tuple([int(r) for r in refl.split()]) + + +def xdi2NXxas(xdidata, h5root, name='entry', compress=None): + """add XDI data to an NXxas group in a NeXuS HDF5 file: + + Arguments + ----------- + xdidata an XDI data group as read with read_xdi() + h5root hdf5 group to add new NXxas group to + name str, name of group under h5root ['entry'] + compress dict, options for compression of array datasets [None] + + + Notes + ------ + 1 the default compress dict is {'compression': 'gzip', 'compression_opts': 4} + """ + if compress is None: + compress = {'compression': 'gzip', 'compression_opts': 4} + + if name in h5root: + base = name[:] + for i in range(1, 100000): + name = f'{base}_{i:02d}' + if name not in h5root: + break + entry_name = name + xas = h5root.create_group(entry_name) + xas.attrs['NX_class'] = 'NXentry' + + xdi_scan = xdidata.attrs.get('scan', {}) + xdi_mono = xdidata.attrs.get('mono', {}) + xdi_dets = xdidata.attrs.get('detectpr', {}) + xdi_sample = xdidata.attrs.get('sample', {}) + xdi_bline = xdidata.attrs.get('beamline', {}) + xdi_facil = xdidata.attrs.get('facility', {}) + + start_time = xdi_scan.get('start_time', '') + + title = xdi_sample.get('name', None) + if title is None: + title = xdidata.filename + title = f'{title} [{xdidata.element} {xdidata.edge} edge]' + + xas.create_dataset('title', data=title) + s =xas.create_dataset('start_time', data=start_time) + s.attrs['NX_class'] = 'NX_DATE_TIME' + s = xas.create_dataset('definition', data='NXxas') + s.attrs['URL'] = NXXAS_URL + + # instrument + instrument = xas.create_group('instrument') + instrument.attrs['NX_class'] = 'NXinstrument' + + # instrument/source + isource = instrument.create_group('source') + isource.attrs['NX_class'] = 'NXsource' + sname = [xdi_facil.get('name', 'unknown facility'), + xdi_facil.get('xray_source', 'unknkown source'), + xdi_bline.get('name', 'unknown beamline')] + isource.create_dataset('name', data=', '.join(sname)) + + source_energy = xdi_facil.get('energy', '0 Unknown') + try: + s_en, units = source_energy.split() + source_energy = float(s_en) + except: + source_energy, units = 0, 'Unknown' + s = isource.create_dataset('energy', data=source_energy) + s.attrs['sunits'] = units + isource.create_dataset('type', data='X-ray Source') + isource.create_dataset('probe', data='X-ray') + for key, val in xdi_facil.items(): + isource.create_dataset(f'facility_{key}', data=val) + for key, val in xdi_bline.items(): + isource.create_dataset(f'beamline_{key}', data=val) + + # instrument/mono + imono = instrument.create_group('monochromator') + imono.attrs['NX_class'] = 'NXmonochromator' + + ien = xdidata.array_labels.index('energy') + en_units = 'unknown' + if ien > -1: + en_units = xdidata.array_units[ien] + s = imono.create_dataset('energy', data=xdidata.energy, **compress) + s.attrs['units'] = en_units + if hasattr(xdidata, 'angle'): + s = imono.create_dataset('angle', data=xdidata.angle, **compress) + s.attrs['units'] = 'degrees' + + # instrument/mono/crystal + imonoxtal = imono.create_group('crystal') + imonoxtal.attrs['NX_class'] = 'NXcrystal' + mono_name = xdi_mono.get('name', 'Si (111)') + try: + mono_chem, mono_refl = mono_name.split() + except: + mono_chem, mono_refl = 'Si', '111' + mono_chem = mono_chem.title() + mono_refl = parse_mono_reflection(mono_refl) + + mono_dspacing = xdi_mono.get('d_spacing', None) + if mono_dspacing is None: + mono_dspacing = 0.0 + if mono_chem in LATTICE_CONSTANTS: + latt_c = LATTICE_CONSTANTS[mono_chem] + hkl2 = mono_refl[0]**2 + mono_refl[1]**2 + mono_refl[2]**2 + mono_dspacing = latt_c / np.sqrt(hkl2) + else: + mono_dspacing = float(mono_dspacing) + + imonoxtal.create_dataset('chemical_formula', data=mono_chem) + imonoxtal.create_dataset('reflection', data=mono_refl) + s = imonoxtal.create_dataset('d_spacing', data=mono_dspacing) + s.attrs['units'] = 'Angstroms' + + # instrument/i0 + idet = instrument.create_group('i0') + idet.attrs['NX_class'] = 'NXdetector' + idet.create_dataset('data', data=xdidata.i0, **compress) + desc = xdi_dets.get('i0', None) + if desc is None: + desc = xdi_dets.get('monitor', None) + if desc is not None: + idet.create_dataset('description', data=desc) + + # instrument/itrans + if hasattr(xdidata, 'itrans'): + idet = instrument.create_group('itrans') + idet.attrs['NX_class'] = 'NXdetector' + idet.create_dataset('data', data=xdidata.itrans, **compress) + desc = xdi_dets.get('itrans', None) + if desc is None: + desc = xdi_dets.get('i1', None) + if desc is not None: + idet.create_dataset('description', data=desc) + + # instrument/ifluor + if hasattr(xdidata, 'ifluor'): + idet = instrument.create_group('ifluor') + idet.attrs['NX_class'] = 'NXdetector' + + idet.create_dataset('data', data=xdidata.ifluor, **compress) + desc = xdi_dets.get('ifluor', None) + if desc is None: + desc = xdi_dets.get('if', None) + if desc is not None: + idet.create_dataset('description', data=desc) + mode = xdi_dets.get('fluor_mode', 'Unknown') + idet.create_dataset('mode', data=mode) + + # instrument/irefer + if hasattr(xdidata, 'irefer'): + idet = instrument.create_group('irefer') + idet.attrs['NX_class'] = 'NXdetector' + + idet.create_dataset('data', data=xdidata.irefer, **compress) + desc = xdi_dets.get('irefer', None) + if desc is None: + desc = xdi_dets.get('ir', None) + if desc is not None: + idet.create_dataset('description', data=desc) + refmode = xdi_dets.get('refer_mode', 'Unknown') + idet.create_dataset('mode', data=mode) + + # sample + sample = xas.create_group('sample') + sample.attrs['NX_class'] = 'NXsample' + for key, val in xdi_sample.items(): + sample.create_dataset(key, data=val) + + # scan + scan = xas.create_group('scan') + scan.attrs['NX_class'] = 'NXscan' + for key, val in xdi_scan.items(): + sample.create_dataset(key, data=val) + ncol, np = xdidata.data.shape + scan.create_dataset('nP', data=np) + scan.create_dataset('nCol', data=ncol) + + xedge = sample.create_group('xrayedge') + xedge.attrs['NX_class'] = 'NXxrayedge' + xedge.create_dataset('element', data=xdidata.element) + xedge.create_dataset('edge', data=xdidata.edge) + + scan.create_dataset('scan_mode', data=xdi_scan.get('mode', 'Unknown')) + scan.create_dataset('data', data=xdidata.data.transpose(), **compress) + scan.create_dataset('column_labels', data=xdidata.array_labels) + + # data arrays: mostly links to the data above + dat = xas.create_group('data') + dat.attrs['NX_class'] = 'NXdata' + mode = 'Transmission' + if not hasattr(xdidata, 'itrans') and hasattr(xdidata, 'ifluor'): + mode = 'Fluorescence' + dat.create_dataset('mode', data=mode) + dat['element'] = h5py.SoftLink(f'/{entry_name}/scan/xrayedge/element') + dat['edge'] = h5py.SoftLink(f'/{entry_name}/scan/xrayedge/edge') + + dat['column_labels'] = h5py.SoftLink(f'/{entry_name}/scan/column_labels') + # dat['rawdata'] = h5py.SoftLink(f'/{entry_name}/scan/data') + + dat['energy'] = h5py.SoftLink(f'/{entry_name}/instrument/monochromator/energy') + dat['i0'] = h5py.SoftLink(f'/{entry_name}/instrument/i0/data') + if hasattr(xdidata, 'itrans'): + dat['itrans'] = h5py.SoftLink(f'/{entry_name}/instrument/itrans/data') + mutrans = -safe_log(xdidata.itrans/xdidata.i0) + dat.create_dataset('mutrans', data=mutrans, **compress) + + if hasattr(xdidata, 'ifluor'): + dat['ifluor'] = h5py.SoftLink(f'/{entry_name}/instrument/ifluor/data') + mufluor = xdidata.ifluor/xdidata.i0 + dat.create_dataset('mufluor', data=mufluor, **compress) + + if hasattr(xdidata, 'irefer'): + dat['irefer'] = h5py.SoftLink(f'/{entry_name}/instrument/irefer/data') + if refmode.startswith('Fluo'): + muref = xdidata.irefer/xdidata.i0 + else: + muref = -safe_log(xdidata.irefer/xdidata.itrans) + dat.create_dataset('murefer', data=murefer, **compress) + + +class NXxasFile(object): + """ + NeXuS NXxas data file + """ + def __init__(self, filename): + self.filename = filename + self.xas_groups = None + if filename is not None: + self.read(filename) + + def read(self, filename): + self.root = h5py.File(filename, 'r') + self.xas_groups = [] + for key, grp in self.root.items(): + attrs = grp.attrs.items() + if grp.attrs.get('NX_class', None) != 'NXentry': + continue + defn = grp.get('definition', None) + if isinstance(defn, h5py.Dataset): + if bytes2str(defn[()]) == 'NXxas': + self.xas_groups.append(key) + + def add_xdidata(xdidata, name='entry'): + """add Entry/Group for data from an XDI file""" + xdi2NXxas(xdidata, self.root, name=name) From f5c7746437b5b9f432787972f8a78410ae4d0d80 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Tue, 1 Aug 2023 23:07:51 -0500 Subject: [PATCH 02/13] more uniform location for atomic symbols and element names --- larch/utils/physical_constants.py | 63 ++++++++++++++++--------------- larch/wxlib/feff_browser.py | 6 +-- larch/wxxas/config.py | 4 +- larch/wxxas/xas_dialogs.py | 15 +------- larch/xray/__init__.py | 16 ++------ larch/xrd/amcsd.py | 16 +------- larch/xrd/amcsd_utils.py | 37 +----------------- 7 files changed, 46 insertions(+), 111 deletions(-) diff --git a/larch/utils/physical_constants.py b/larch/utils/physical_constants.py index bbe190a00..37ae5f8e4 100644 --- a/larch/utils/physical_constants.py +++ b/larch/utils/physical_constants.py @@ -32,42 +32,43 @@ # Rydberg constant in eV (~13.6 eV) RYDBERG = consts.Rydberg * consts.Planck * consts.c/ consts.e -# classical electron radius in cm +# classical electron radius in cm and Ang R_ELECTRON_CM = 100.0 * consts.physical_constants['classical electron radius'][0] -R_ELECTRON_ANG = 1.e10 * consts.physical_constants['classical electron radius'][0] +R_ELECTRON_ANG = 1.e8 * R_ELECTRON_CM ATOM_SYMS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', - 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', - 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', - 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', - 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', - 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', - 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', - 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', - 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', - 'Md', 'No', 'Lr'] + 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', + 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', + 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', + 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', + 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', + 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', + 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', + 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', 'Md', + 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', 'Rg', 'Cn', + 'Nh', 'Fl', 'Mc', 'Lv', 'Ts', 'Og'] - -ATOM_NAMES = ['hydrogen', 'helium', 'lithium', 'beryllium', 'boron', - 'carbon', 'nitrogen', 'oxygen', 'fluorine', 'neon', 'sodium', - 'magnesium', 'aluminum', 'silicon', 'phosphorus', 'sulfur', - 'chlorine', 'argon', 'potassium', 'calcium', 'scandium', - 'titanium', 'vanadium', 'chromium', 'manganese', 'iron', - 'cobalt', 'nickel', 'copper', 'zinc', 'gallium', 'germanium', - 'arsenic', 'selenium', 'bromine', 'krypton', 'rubidium', - 'strontium', 'yttrium', 'zirconium', 'niobium', 'molybdenum', - 'technetium', 'ruthenium', 'rhodium', 'palladium', 'silver', - 'cadmium', 'indium', 'tin', 'antimony', 'tellurium', 'iodine', - 'xenon', 'cesium', 'barium', 'lanthanum', 'cerium', - 'praseodymium', 'neodymium', 'promethium', 'samarium', +ATOM_NAMES = ['hydrogen', 'helium', 'lithium', 'beryllium', 'boron', 'carbon', + 'nitrogen', 'oxygen', 'fluorine', 'neon', 'sodium', 'magnesium', + 'aluminum', 'silicon', 'phosphorus', 'sulfur', 'chlorine', 'argon', + 'potassium', 'calcium', 'scandium', 'titanium', 'vanadium', + 'chromium', 'manganese', 'iron', 'cobalt', 'nickel', 'copper', + 'zinc', 'gallium', 'germanium', 'arsenic', 'selenium', 'bromine', + 'krypton', 'rubidium', 'strontium', 'yttrium', 'zirconium', + 'niobium', 'molybdenum', 'technetium', 'ruthenium', 'rhodium', + 'palladium', 'silver', 'cadmium', 'indium', 'tin', 'antimony', + 'tellurium', 'iodine', 'xenon', 'cesium', 'barium', 'lanthanum', + 'cerium', 'praseodymium', 'neodymium', 'promethium', 'samarium', 'europium', 'gadolinium', 'terbium', 'dysprosium', 'holmium', 'erbium', 'thulium', 'ytterbium', 'lutetium', 'hafnium', - 'tantalum', 'tungsten', 'rhenium', 'osmium', 'iridium', - 'platinum', 'gold', 'mercury', 'thallium', 'lead', 'bismuth', - 'polonium', 'astatine', 'radon', 'francium', 'radium', - 'actinium', 'thorium', 'protactinium', 'uranium', 'neptunium', - 'plutonium', 'americium', 'curium', 'berkelium', 'californium', - 'einsteinium', 'fermium', 'mendelevium', 'nobelium', - 'lawrencium'] + 'tantalum', 'tungsten', 'rhenium', 'osmium', 'iridium', 'platinum', + 'gold', 'mercury', 'thallium', 'lead', 'bismuth', 'polonium', + 'astatine', 'radon', 'francium', 'radium', 'actinium', 'thorium', + 'protactinium', 'uranium', 'neptunium', 'plutonium', 'americium', + 'curium', 'berkelium', 'californium', 'einsteinium', 'fermium', + 'mendelevium', 'nobelium', 'lawrencium', 'rutherfordium', + 'dubnium', 'seaborgium', 'bohrium', 'hassium', 'meitnerium', + 'darmstadtium', 'roentgenium', 'copernicium', 'nihonium', + 'flerovium', 'moscovium', 'livermorium', 'tennessine', 'oganesson'] diff --git a/larch/wxlib/feff_browser.py b/larch/wxlib/feff_browser.py index 2883f0521..695b8578e 100644 --- a/larch/wxlib/feff_browser.py +++ b/larch/wxlib/feff_browser.py @@ -18,9 +18,9 @@ LarchWxApp) from larch.xafs import get_feff_pathinfo -from larch.xray import atomic_symbols +from larch.utils.physical_constants import ATOM_SYMS -ATSYMS = ['< All Atoms>'] + atomic_symbols +ATSYMS = ['< All Atoms>'] + ATOM_SYMS[:96] EDGES = ['< All Edges>', 'K', 'L3', 'L2', 'L1', 'M5'] @@ -143,7 +143,7 @@ def GetResponse(self): class FeffResultsPanel(wx.Panel): """ present Feff results """ - def __init__(self, parent=None, feffresult=None, path_importer=None, + def __init__(self, parent=None, feffresult=None, path_importer=None, _larch=None): wx.Panel.__init__(self, parent, -1, size=(700, 500)) self.parent = parent diff --git a/larch/wxxas/config.py b/larch/wxxas/config.py index 4433a8a0a..8289d19f7 100644 --- a/larch/wxxas/config.py +++ b/larch/wxxas/config.py @@ -2,9 +2,9 @@ from larch.site_config import get_homedir from larch.xafs.xafsutils import FT_WINDOWS -from larch.xray import atomic_symbols +from larch.utils.physical_constants import ATOM_SYMS -ATSYMS = ['?'] + atomic_symbols +ATSYMS = ['?'] + ATOM_SYMS[:98] EDGES = ['K', 'L3', 'L2', 'L1', 'M5', 'M4', 'M3', 'N7'] CONF_FILE = 'xas_viewer.conf' diff --git a/larch/wxxas/xas_dialogs.py b/larch/wxxas/xas_dialogs.py index f12054c0e..0826d4425 100644 --- a/larch/wxxas/xas_dialogs.py +++ b/larch/wxxas/xas_dialogs.py @@ -24,23 +24,12 @@ get_zoomlimits, set_zoomlimits) from larch.xafs import etok, ktoe, find_energy_step -from larch.utils.physical_constants import PI, DEG2RAD, PLANCK_HC +from larch.utils.physical_constants import PI, DEG2RAD, PLANCK_HC, ATOM_SYMS from larch.math import smooth Plot_Choices = {'Normalized': 'norm', 'Derivative': 'dmude'} -ELEM_LIST = ('H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', - 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', - 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', - 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', - 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', - 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', - 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', - 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', - 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', - 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf') - EDGE_LIST = ('K', 'L3', 'L2', 'L1', 'M5', 'M4', 'M3') NORM_MU = 'Normalized \u03BC(E)' @@ -115,7 +104,7 @@ def __init__(self, parent, controller, **kws): wids['phi_in'] = FloatSpin(panel, **fs_opts) wids['phi_out'] = FloatSpin(panel, **fs_opts) - wids['elem'] = Choice(panel, choices=ELEM_LIST, size=(50, -1)) + wids['elem'] = Choice(panel, choices=ATOM_SYMS[:98], size=(50, -1)) wids['edge'] = Choice(panel, choices=EDGE_LIST, size=(50, -1)) wids['formula'] = wx.TextCtrl(panel, -1, '', size=(250, -1)) diff --git a/larch/xray/__init__.py b/larch/xray/__init__.py index de1b9827a..06ff1ef57 100644 --- a/larch/xray/__init__.py +++ b/larch/xray/__init__.py @@ -19,6 +19,8 @@ xray_lines X-ray emission lines for an element """ +from larch.utils.physical_constants import ATOM_SYMS + from xraydb import (XrayDB, atomic_mass, atomic_number, atomic_symbol, atomic_density, xray_line, xray_lines, xray_edge, xray_edges, ck_probability, f0, f0_ions, mu_elam, @@ -36,18 +38,6 @@ # from .cromer_liberman import f1f2 as f1f2_cl from .background import XrayBackground -atomic_symbols = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', - 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', - 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', - 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', - 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', - 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', - 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', - 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', - 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', - 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', - 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf'] - _larch_builtins = {'_xray': dict(chemparse=chemparse, material_get=material_get, material_add=material_add, @@ -86,4 +76,4 @@ def _larch_init(_larch): setsym = _larch.symtable.set_symbol setsym('_xray._xraydb', XrayDB()) setsym('_xray._materials', _read_materials_db()) - setsym('_xray._atomic_symbols', atomic_symbols) + setsym('_xray._atomic_symbols', ATOM_SYMS) diff --git a/larch/xrd/amcsd.py b/larch/xrd/amcsd.py index c2744f17d..d25283a2b 100755 --- a/larch/xrd/amcsd.py +++ b/larch/xrd/amcsd.py @@ -57,7 +57,7 @@ from .cif2feff import cif2feffinp from ..utils import isotime from ..utils.strutils import version_ge, bytes2str -from ..utils.physical_constants import TAU +from ..utils.physical_constants import TAU, ATOM_SYMS from ..site_config import user_larchdir from .. import logger @@ -89,18 +89,6 @@ 'f2hkl', 'degen', 'lorentz')) -elem_symbol = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', - 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', - 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', - 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', - 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', - 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', - 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', - 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', - 'Es', 'Fm', 'Md', 'No', 'Lr', 'Rf', 'Db', 'Sg', 'Bh', 'Hs', 'Mt', 'Ds', - 'Rg', 'Uub', 'Uut', 'Uuq', 'Uup', 'Uuh', 'Uus', 'Uuo'] - - # for packing/unpacking H, K, L to 2-character hash HKL_ENCODE = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_%' def pack_hkl(h, k, l): @@ -1273,7 +1261,7 @@ def find_cifs(self, id=None, mineral_name=None, author_name=None, matches = new_matches if strict_contains: - excludes_elements = elem_symbol[:] + excludes_elements = ATOM_SYMS[:] for c in contains_elements: if c in excludes_elements: excludes_elements.remove(c) diff --git a/larch/xrd/amcsd_utils.py b/larch/xrd/amcsd_utils.py index 4062086b4..12d00e102 100644 --- a/larch/xrd/amcsd_utils.py +++ b/larch/xrd/amcsd_utils.py @@ -9,7 +9,7 @@ from sqlalchemy.orm import sessionmaker from sqlalchemy.pool import SingletonThreadPool - +from larch.utils.physical_constants import ATOM_SYMS, ATOM_NAMES __version__ = '1' @@ -173,39 +173,6 @@ def get_optarray(dat): ) -atsyms = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', - 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', - 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', - 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', - 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', - 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', - 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', - 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', - 'Th', 'Pa', 'U', 'Np', 'Pu', 'Am', 'Cm', 'Bk', 'Cf', 'Es', 'Fm', - 'Md', 'No', 'Lr', 'D'] - - -atnames = ['hydrogen', 'helium', 'lithium', 'beryllium', - 'boron', 'carbon', 'nitrogen', 'oxygen', 'fluorine', 'neon', - 'sodium', 'magnesium', 'aluminum', 'silicon', 'phosphorus', - 'sulfur', 'chlorine', 'argon', 'potassium', 'calcium', 'scandium', - 'titanium', 'vanadium', 'chromium', 'manganese', 'iron', 'cobalt', - 'nickel', 'copper', 'zinc', 'gallium', 'germanium', 'arsenic', - 'selenium', 'bromine', 'krypton', 'rubidium', 'strontium', - 'yttrium', 'zirconium', 'niobium', 'molybdenum', 'technetium', - 'ruthenium', 'rhodium', 'palladium', 'silver', 'cadmium', - 'indium', 'tin', 'antimony', 'tellurium', 'iodine', 'xenon', - 'cesium', 'barium', 'lanthanum', 'cerium', 'praseodymium', - 'neodymium', 'promethium', 'samarium', 'europium', 'gadolinium', - 'terbium', 'dysprosium', 'holmium', 'erbium', 'thulium', - 'ytterbium', 'lutetium', 'hafnium', 'tantalum', 'tungsten', - 'rhenium', 'osmium', 'iridium', 'platinum', 'gold', 'mercury', - 'thallium', 'lead', 'bismuth', 'polonium', 'astatine', 'radon', - 'francium', 'radium', 'actinium', 'thorium', 'protactinium', - 'uranium', 'neptunium', 'plutonium', 'americium', 'curium', - 'berkelium', 'californium', 'einsteinium', 'fermium', - 'mendelevium', 'nobelium', 'lawrencium', 'deuterium' ] - def create_amcsd(dbname='test.db'): if os.path.exists(dbname): os.unlink(dbname) @@ -219,7 +186,7 @@ def create_amcsd(dbname='test.db'): ('0', 'in progress', 'today', 'in progress')) atz, i = 0, 0 - for sym, name in zip(atsyms, atnames): + for sym, name in zip(ATOM_SYMS, ATOM_NAMES): i += 1 atz += 1 if sym == 'D': From 06cb4afb5b2ba7b825ec17c6d5a8a09827545a23 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Thu, 3 Aug 2023 17:17:01 -0500 Subject: [PATCH 03/13] add common lattice constants --- larch/utils/physical_constants.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/larch/utils/physical_constants.py b/larch/utils/physical_constants.py index 37ae5f8e4..469f9896e 100644 --- a/larch/utils/physical_constants.py +++ b/larch/utils/physical_constants.py @@ -37,7 +37,11 @@ R_ELECTRON_ANG = 1.e8 * R_ELECTRON_CM +# a few standard lattice constants +STD_LATTICE_CONSTANTS = {'Si': 5.4310205, 'C': 3.567095, 'Ge': 5.64613} + +# will be able to import these from xraydb when v 4.5.1 is required ATOM_SYMS = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mn', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', From d4b76e5a5e4dedf64b546948f2169cfa6db58897 Mon Sep 17 00:00:00 2001 From: Matt Newville Date: Thu, 3 Aug 2023 17:17:39 -0500 Subject: [PATCH 04/13] wip: some fixes for nexus creation --- larch/io/nexus_xas.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/larch/io/nexus_xas.py b/larch/io/nexus_xas.py index bb85ed96d..f0f8ad0a7 100644 --- a/larch/io/nexus_xas.py +++ b/larch/io/nexus_xas.py @@ -2,17 +2,14 @@ import h5py from larch.io import read_xdi from larch.utils.strutils import bytes2str +from larch.math.utils import safe_log +from larch.utils.physical_constants import STD_LATTICE_CONSTANTS NXXAS_URL = 'https://download.nexusformat.org/doc/html/classes/applications/NXxas.html' -LATTICE_CONSTANTS = {'Si': 5.4310205, 'C': 3.567095, 'Ge': 5.64613} - -def safe_log(x, extreme=50): - return np.log(np.clip(x, np.e**-extreme, np.e**extreme)) - def parse_mono_reflection(refl): - refl = refl.replace(',', ' ') - if refl.startswith('(') and refl.startswith(')'): + refl = refl.replace(',', ' ').strip() + if refl.startswith('(') and refl.endswith(')'): refl = refl[1:-1] if len(refl) == 3: return tuple([int(refl[0]), int(refl[1]), int(refl[2])]) @@ -86,7 +83,7 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): except: source_energy, units = 0, 'Unknown' s = isource.create_dataset('energy', data=source_energy) - s.attrs['sunits'] = units + s.attrs['units'] = units isource.create_dataset('type', data='X-ray Source') isource.create_dataset('probe', data='X-ray') for key, val in xdi_facil.items(): @@ -118,12 +115,11 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): mono_chem, mono_refl = 'Si', '111' mono_chem = mono_chem.title() mono_refl = parse_mono_reflection(mono_refl) - mono_dspacing = xdi_mono.get('d_spacing', None) if mono_dspacing is None: mono_dspacing = 0.0 - if mono_chem in LATTICE_CONSTANTS: - latt_c = LATTICE_CONSTANTS[mono_chem] + if mono_chem in STD_LATTICE_CONSTANTS: + latt_c = STD_LATTICE_CONSTANTS[mono_chem] hkl2 = mono_refl[0]**2 + mono_refl[1]**2 + mono_refl[2]**2 mono_dspacing = latt_c / np.sqrt(hkl2) else: @@ -194,11 +190,11 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): scan.attrs['NX_class'] = 'NXscan' for key, val in xdi_scan.items(): sample.create_dataset(key, data=val) - ncol, np = xdidata.data.shape - scan.create_dataset('nP', data=np) + ncol, nrow = xdidata.data.shape + scan.create_dataset('nP', data=nrow) scan.create_dataset('nCol', data=ncol) - xedge = sample.create_group('xrayedge') + xedge = scan.create_group('xrayedge') xedge.attrs['NX_class'] = 'NXxrayedge' xedge.create_dataset('element', data=xdidata.element) xedge.create_dataset('edge', data=xdidata.edge) From 3cd1fda34960d7d7fee6ddf08131e6e16afc2153 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 12:49:48 -0500 Subject: [PATCH 05/13] cleanup --- larch/wxlib/gui_utils.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/larch/wxlib/gui_utils.py b/larch/wxlib/gui_utils.py index 208c64a28..459815bc9 100644 --- a/larch/wxlib/gui_utils.py +++ b/larch/wxlib/gui_utils.py @@ -67,33 +67,6 @@ def InitLocale(self): def run(self): self.MainLoop() -# def panel_pack(window, panel, pad=10): -# """ -# a simple method to pack a single panel to a single frame -# """ -# sizer = wx.BoxSizer(wx.VERTICAL) -# sizer.Add(panel, 1, wx.LEFT, 5) -# window.SetSizer(sizer) -# sizer.Fit(window) -# w0, h0 = window.GetSize() -# w1, h1 = window.GetBestSize() -# window.SetSize((max(w0, w1)+pad, max(h0, h1)+pad)) -# -# def show_wxsizes(obj): -# """recursively show sizes of wxPython objects -- -# useful for avoiding size<1 errors""" -# for child in obj.GetChildren(): -# try: -# csize = child.GetSize() -# if csize[0] < 1 or csize[1] < 1: -# print(child, csize) -# except: -# pass -# try: -# show_wxsizes(child) -# except: -# pass - def wx_update(_larch=None, **kws): """force an update of wxPython windows""" symtable = ensuremod(_larch, '_sys') From aad74640523c43b2b111a792aeb10820c46e2674 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 12:52:29 -0500 Subject: [PATCH 06/13] deprecating XRD1DViewer for XR1DFrame and XRD1DApp --- larch/wxmap/mapviewer.py | 9 +++++---- larch/wxxrd/XRD1Dviewer.py | 11 ++++++----- larch/wxxrd/__init__.py | 5 ++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/larch/wxmap/mapviewer.py b/larch/wxmap/mapviewer.py index a1f9bfd3b..7df48b256 100644 --- a/larch/wxmap/mapviewer.py +++ b/larch/wxmap/mapviewer.py @@ -61,8 +61,8 @@ from .maptomopanel import TomographyPanel from .mapxrfpanel import XRFAnalysisPanel -from ..wxxrd import XRD1DViewerFrame, XRD2DViewerFrame -from ..wxxrd.xrd1d_display import XRD1DBrowserFrame +from ..wxxrd import XRD2DViewerFrame +from ..wxxrd.xrd1d_display import XRD1DFrame def timestring(): return datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f') @@ -1780,11 +1780,12 @@ def display_xrd1d(self, counts, q, energy, label='dataset 0', xrmfile=None): xdat = xrd1d(label=label, energy=energy, wavelength=wavelength) xdat.set_xy_data(np.array([q, counts]), 'q') if self.xrddisplay1D is None: - self.xrddisplay1D = XRD1DBrowserFrame(wavelength=wavelength, _larch=self.larch) + self.xrddisplay1D = XRD1DFrame(wavelength=wavelength, + _larch=self.larch) try: self.xrddisplay1D.add_data(xdat, label=label) except: - self.xrddisplay1D = XRD1DBrowserFrame(_larch=self.larch) + self.xrddisplay1D = XRD1DFrame(_larch=self.larch) self.xrddisplay1D.add_data(xdat, label=label) self.xrddisplay1D.Show() diff --git a/larch/wxxrd/XRD1Dviewer.py b/larch/wxxrd/XRD1Dviewer.py index f641eb6b4..502270c03 100644 --- a/larch/wxxrd/XRD1Dviewer.py +++ b/larch/wxxrd/XRD1Dviewer.py @@ -31,13 +31,14 @@ from larch.utils import nativepath, get_cwd from larch.wxlib import PeriodicTablePanel, Choice, LarchWxApp -from larch.xrd import (cifDB, SearchCIFdb, QSTEP, QMIN, QMAX, CATEGORIES, - match_database, d_from_q,twth_from_q,q_from_twth, +from larch.xrd.cifdb import (cifDB, SearchCIFdb, QSTEP, QMIN, QMAX, CATEGORIES, + match_database, SPACEGROUPS, create_xrdcif) + +from larch.xrd import (d_from_q,twth_from_q,q_from_twth, d_from_twth,twth_from_d,q_from_d, lambda_from_E, E_from_lambda,calc_broadening, - instrumental_fit_uvw,peaklocater,peakfitter, - xrd1d, peakfinder_methods,SPACEGROUPS, create_xrdcif, - save1D) + instrumental_fit_uvw,peaklocater,peakfitter, xrd1d, + peakfinder_methods, save1D) ################################### diff --git a/larch/wxxrd/__init__.py b/larch/wxxrd/__init__.py index 4a3ebe7bc..511e1f74c 100644 --- a/larch/wxxrd/__init__.py +++ b/larch/wxxrd/__init__.py @@ -1,5 +1,8 @@ from .XRD2Dviewer import XRD2DViewerFrame, XRD2DViewer -from .XRD1Dviewer import XRD1DViewerFrame, XRD1DViewer, Calc1DPopup, plot_sticks +# from .XRD1Dviewer import XRD1DViewerFrame, XRD1DViewer, Calc1DPopup, plot_sticks + +from .xrd1d_display import XRD1DApp + from .ImageControlsFrame import ImageToolboxFrame from .XRDCalibrationFrame import CalibrationPopup, CalXRD from .XRDMaskFrame import MaskToolsPopup From 0cd95c825010a1e748932d61b18378ecc167b3d5 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 12:53:19 -0500 Subject: [PATCH 07/13] moving to amcsd, no longer importing xrd_cif by default --- larch/xrd/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/larch/xrd/__init__.py b/larch/xrd/__init__.py index 9308e2135..e7b1287a3 100644 --- a/larch/xrd/__init__.py +++ b/larch/xrd/__init__.py @@ -18,10 +18,6 @@ q_from_twth, qv_from_hkl, d_from_hkl, unit_cell_volume, generate_hkl) -from .xrd_cif import (SPACEGROUPS, create_xrdcif, check_elemsym, SPGRP_SYMM) - -from .cifdb import (get_cifdb, cifDB, cif_match, read_cif, SearchCIFdb, - match_database, CATEGORIES, QSTEP, QMIN, QMAX, QAXIS) from .amcsd import CifStructure, get_amcsd, get_cif, find_cifs, parse_cif_file @@ -56,9 +52,7 @@ 'generate_hkl': generate_hkl, 'xrd_background': xrd_background, 'integrate_xrd': integrate_xrd, - 'cif_match': cif_match, - 'get_cifdb': get_cifdb, - 'read_cif': read_cif, + 'create_xrd': create_xrd, 'create_xrd1d': create_xrd1d, 'peakfinder': peakfinder, @@ -73,6 +67,13 @@ 'find_cifs': find_cifs, }} + +### from .xrd_cif import (SPACEGROUPS, create_xrdcif, check_elemsym, SPGRP_SYMM) +# from .cifdb import (get_cifdb, cifDB, cif_match, read_cif, SearchCIFdb, +# match_database, CATEGORIES, QSTEP, QMIN, QMAX, QAXIS) + # 'cif_match': cif_match, + # 'get_cifdb': get_cifdb, + # 'read_cif': read_cif, # 'data_gaussian_fit': data_gaussian_fit, # 'gaussian': gaussian, # 'doublegaussian': doublegaussian, From c6638d2a3df64f562e9e1c4dfc020e7f49e54672 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 12:54:13 -0500 Subject: [PATCH 08/13] better reading of XY files, PONI files, standalone app --- larch/wxxrd/xrd1d_display.py | 102 ++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/larch/wxxrd/xrd1d_display.py b/larch/wxxrd/xrd1d_display.py index f58d2180d..735a71e13 100644 --- a/larch/wxxrd/xrd1d_display.py +++ b/larch/wxxrd/xrd1d_display.py @@ -29,13 +29,13 @@ from larch.utils import nativepath, get_cwd, gformat, fix_filename from larch.utils.physical_constants import PLANCK_HC from larch.xray import XrayBackground -from larch.xrd import (cifDB, SearchCIFdb, QSTEP, QMIN, QMAX, CATEGORIES, - match_database, d_from_q,twth_from_q,q_from_twth, +from larch.wxxas import RemoveDialog + +from larch.xrd import (d_from_q,twth_from_q,q_from_twth, d_from_twth,twth_from_d,q_from_d, lambda_from_E, E_from_lambda, calc_broadening, - instrumental_fit_uvw,peaklocater,peakfitter, - xrd1d, peakfinder_methods,SPACEGROUPS, create_xrdcif, - save1D, read_poni) + instrumental_fit_uvw,peaklocater,peakfitter, xrd1d, + peakfinder_methods, save1D, read_poni) from larch.wxlib import (ReportFrame, BitmapButton, FloatCtrl, FloatSpin, @@ -44,7 +44,8 @@ set_color, CEN, RIGHT, LEFT, FRAMESTYLE, Font, FONTSIZE, FONTSIZE_FW, FileSave, FileOpen, flatnotebook, Popup, FileCheckList, - EditableListBox, ExceptionPopup, CIFFrame) + EditableListBox, ExceptionPopup, CIFFrame, + LarchFrame, LarchWxApp) XYWcards = "XY Data File(*.xy)|*.xy|All files (*.*)|*.*" PlotWindowChoices = ['1', '2', '3', '4', '5', '6', '7', '8', '9'] @@ -125,7 +126,6 @@ def extract_background(x, y, smooth_width=0.1, iterations=40, cheb_order=40): :return: vector of extracted y background """ smooth_points = int((float(smooth_width) / (x[1] - x[0]))) - # print('bkg ', smooth_points, iterations,cheb_order) y_smooth = smooth_bruckner(y, abs(smooth_points), iterations) # get cheb input parameters x_cheb = 2. * (x - x[0]) / (x[-1] - x[0]) - 1. @@ -137,7 +137,7 @@ def calc_bgr(dset, qwid=0.1, nsmooth=40, cheb_order=40): iterations=nsmooth, cheb_order=cheb_order) -class XRD1DBrowserFrame(wx.Frame): +class XRD1DFrame(wx.Frame): """browse 1D XRD patterns""" def __init__(self, parent=None, wavelength=1.0, ponifile=None, _larch=None, **kws): @@ -153,6 +153,15 @@ def __init__(self, parent=None, wavelength=1.0, ponifile=None, _larch=None, **kw except: pass self.larch = _larch + if self.larch is None: + self.larch_buffer = LarchFrame(_larch=None, parent=self, + is_standalone=False, + with_raise=False, + exit_on_close=False) + + self.larch = self.larch_buffer.larchshell + + self.current_label = None self.cif_browser = None self.datasets = {} @@ -163,6 +172,7 @@ def __init__(self, parent=None, wavelength=1.0, ponifile=None, _larch=None, **kw def createMenus(self): fmenu = wx.Menu() + cmenu = wx.Menu() MenuItem(self, fmenu, "Read XY File", "Read XRD 1D data from XY FIle", self.onReadXY) @@ -172,39 +182,47 @@ def createMenus(self): self.onSaveXY) fmenu.AppendSeparator() - MenuItem(self, fmenu, "Read PONI Calibration File", - "Read PONI Calibration (pyFAI) FIle", - self.onReadPONI) + MenuItem(self, fmenu, "Remove Selected Patterns", + "Remove Selected Patterns", + self.remove_selected_datasets) - fmenu.AppendSeparator() - MenuItem(self, fmenu, "Browse AmMin Crystal Structures", + MenuItem(self, cmenu, "Browse AmMin Crystal Structures", "Browse Structures from Am Min Database", self.onCIFBrowse) + MenuItem(self, cmenu, "Read PONI Calibration File", + "Read PONI Calibration (pyFAI) FIle", + self.onReadPONI) + menubar = wx.MenuBar() menubar.Append(fmenu, "&File") + menubar.Append(cmenu, "&XRD and CIF Structures") + self.SetMenuBar(menubar) def onReadPONI(self, event=None): sfile = FileOpen(self, 'Read PONI (pyFAI) calibration file', - defaultDir=get_cwd(), + default_file='XRD.poni', + default_dir=get_cwd(), wildcard="PONI Files(*.poni)|*.poni|All files (*.*)|*.*") if sfile is not None: - top, xfile = os.split(sfile) try: - self.poni.update(read_poni(path)) + self.poni.update(read_poni(sfile)) except: - pass - self.set_wavelength(self.poni[wavelength]*1.e10) + title = "Could not read PONI File" + message = [f"Could not read PONI file {sfile}"] + ExceptionPopup(self, title, message) + + self.set_wavelength(self.poni['wavelength']*1.e10) def onReadXY(self, event=None): sfile = FileOpen(self, 'Read XY Data', - defaultDir=get_cwd(), + default_file='XRD.xy', + default_dir=get_cwd(), wildcard=XYWcards) if sfile is not None: - print(' would read ', sfile) - top, xfile = os.split(sfile) + top, xfile = os.path.split(sfile) dxrd = xrd1d(file=sfile) self.add_data(dxrd, label=xfile) @@ -603,8 +621,34 @@ def scale_data(self, dset, with_plot=True): if with_plot: self.onPlotOne() - def remove_dataset(self, event=None): - print('remove dataset ', event.GetString()) + def remove_dataset(self, dname=None, event=None): + if dname in self.datasets: + self.datasets.pop(dname) + + self.filelist.Clear() + for name in self.datasets: + self.filelist.Append(name) + + def remove_selected_datasets(self, event=None): + sel = [] + for checked in self.filelist.GetCheckedStrings(): + sel.append(str(checked)) + if len(sel) < 1: + return + + dlg = RemoveDialog(self, sel) + res = dlg.GetResponse() + dlg.Destroy() + + if res.ok: + all_names = self.filelist.GetItems() + for dname in sel: + self.datasets.pop(dname) + all_fnames.remove(dname) + + filelist.Clear() + for name in all_names: + filelist.Append(name) def get_display(self, win=1, stacked=False): wintitle='XRD Plot Window %i' % win @@ -672,8 +716,18 @@ def add_data(self, dataset, label=None, **kws): if label is None: label = 'XRD pattern' if label in self.datasets: - print('label alread in datasets: ', label ) + print('label already in datasets: ', label ) else: self.filelist.Append(label) self.datasets[label] = dataset self.show_dataset(label=label) + +class XRD1DApp(LarchWxApp): + def __init__(self, **kws): + LarchWxApp.__init__(self) + + def createApp(self): + frame = XRD1DFrame() + frame.Show() + self.SetTopWindow(frame) + return True From 305f669406c7591c82a96978d32ce604265ac046 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 17:37:01 -0500 Subject: [PATCH 09/13] add set_wavelength method --- larch/xrd/xrd.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/larch/xrd/xrd.py b/larch/xrd/xrd.py index 1d54bdf1b..1ed8b7a4e 100644 --- a/larch/xrd/xrd.py +++ b/larch/xrd/xrd.py @@ -127,7 +127,7 @@ def __init__(self, file=None, label=None, x=None, xtype=None, I=None, def xrd_from_2d(self,xy,xtype,verbose=True): self.set_xy_data(xy, xtype) - def xrd_from_file(self, filename, verbose=True): + def xrd_from_file(self, filename, verbose=False): try: from ..xrmmap.asciifiles import read1DXRDFile @@ -192,6 +192,11 @@ def set_xy_data(self, xy, xtype): self.imin,self.imax = 0,len(self.q) self.bkgd = np.zeros(np.shape(self.I)) + def set_wavelength(self, wavelength): + self.wavelength = wavelength + self.energy = E_from_lambda(self.wavelength) + self.q, self.twth, self.d = calculate_xvalues(self.q, 'q', self.wavelength) + def plot(self,reset=False,bkgd=False): if reset: self.imin,self.imax = 0,len(self.I) @@ -210,7 +215,7 @@ def plot(self,reset=False,bkgd=False): def reset_bkgd(self): self.bkgd = np.zeros(np.shape(self.I)) - def slct_xaxis(self,xtype='',xi=None): + def slct_xaxis(self, xtype='', xi=None): if xtype.startswith('q') or xi == 0: x = self.q @@ -422,7 +427,7 @@ def save_2D(self,file=None,verbose=False): ########################################################################## # FUNCTIONS -def calculate_xvalues(x,xtype,wavelength): +def calculate_xvalues(x, xtype, wavelength): ''' projects given x-axis onto q-, 2theta-, and d-axes @@ -435,7 +440,6 @@ def calculate_xvalues(x,xtype,wavelength): x = np.array(x).squeeze() if xtype.startswith('q'): - q = x d = d_from_q(q) if wavelength is not None: @@ -444,7 +448,6 @@ def calculate_xvalues(x,xtype,wavelength): twth = np.zeros(len(q)) elif xtype.startswith('2th'): - twth = x if wavelength is not None: q = q_from_twth(twth,wavelength) From 5f4c68b8f040ccb597548a4f75eb268d3a267e70 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sat, 5 Aug 2023 17:37:32 -0500 Subject: [PATCH 10/13] wavelength/energy dialog, more improvements --- larch/wxxrd/xrd1d_display.py | 116 +++++++++++++++++++++++++++++------ 1 file changed, 96 insertions(+), 20 deletions(-) diff --git a/larch/wxxrd/xrd1d_display.py b/larch/wxxrd/xrd1d_display.py index 735a71e13..75b1faaf6 100644 --- a/larch/wxxrd/xrd1d_display.py +++ b/larch/wxxrd/xrd1d_display.py @@ -7,7 +7,7 @@ from os.path import expanduser import numpy as np -from numpy.polynomial.chebyshev import chebfit +from numpy.polynomial.chebyshev import chebfit, chebval import sys import time import re @@ -130,12 +130,73 @@ def extract_background(x, y, smooth_width=0.1, iterations=40, cheb_order=40): # get cheb input parameters x_cheb = 2. * (x - x[0]) / (x[-1] - x[0]) - 1. cheb_params = chebfit(x_cheb, y_smooth, cheb_order) - return np.polynomial.chebyshev.chebval(x_cheb, cheb_params) + return chebval(x_cheb, cheb_params) def calc_bgr(dset, qwid=0.1, nsmooth=40, cheb_order=40): return extract_background(dset.q, dset.I, smooth_width=qwid, iterations=nsmooth, cheb_order=cheb_order) +class WavelengthDialog(wx.Dialog): + """dialog for smoothing data""" + def __init__(self, parent, wavelength, callback=None): + + self.parent = parent + self.callback = callback + + wx.Dialog.__init__(self, parent, wx.ID_ANY, size=(550, 400), + title="Set Wavelength / Energy") + self.SetFont(Font(FONTSIZE)) + panel = GridPanel(self, ncols=3, nrows=4, pad=4, itemstyle=LEFT) + + self.wids = wids = {} + + opts = dict(size=(90, -1), act_on_losefocus=True) + wids['wavelength'] = FloatCtrl(panel, value=wavelength, precision=7, + minval=1.0e-4, maxval=100, **opts) + + en_ev = PLANCK_HC/wavelength + wids['energy'] = FloatCtrl(panel, value=en_ev, precision=2, + minval=50, maxval=5.e5, **opts) + + + wids['wavelength'].SetAction(self.set_wavelength) + wids['energy'].SetAction(self.set_energy) + + panel.Add(SimpleText(panel, 'Wavelength(\u212B): '), + dcol=1, newrow=False) + panel.Add(wids['wavelength'], dcol=1) + panel.Add(SimpleText(panel, 'Energy (eV): '), + dcol=1, newrow=True) + panel.Add(wids['energy'], dcol=1) + + panel.Add((10, 10), newrow=True) + + panel.Add(Button(panel, 'Done', size=(150, -1), + action=self.onDone), newrow=True) + panel.pack() + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.Add(panel, 1, LEFT, 5) + pack(self, sizer) + self.Fit() + + w0, h0 = self.GetSize() + w1, h1 = self.GetBestSize() + self.SetSize((max(w0, w1)+25, max(h0, h1)+25)) + + def onDone(self, event=None): + if callable(self.callback): + self.callback(self.wids['wavelength'].GetValue()) + self.Destroy() + + def set_wavelength(self, value=1, event=None): + w = self.wids['wavelength'].GetValue() + + self.wids['energy'].SetValue(PLANCK_HC/w, act=False) + + def set_energy(self, value=10000, event=None): + w = self.wids['energy'].GetValue() + self.wids['wavelength'].SetValue(PLANCK_HC/w, act=False) + class XRD1DFrame(wx.Frame): """browse 1D XRD patterns""" @@ -189,6 +250,10 @@ def createMenus(self): MenuItem(self, cmenu, "Browse AmMin Crystal Structures", "Browse Structures from Am Min Database", self.onCIFBrowse) + fmenu.AppendSeparator() + MenuItem(self, cmenu, "Set Energy / Wavelength", + "Set Energy and Wavelength", + self.onSetWavelength) MenuItem(self, cmenu, "Read PONI Calibration File", "Read PONI Calibration (pyFAI) FIle", @@ -200,6 +265,11 @@ def createMenus(self): self.SetMenuBar(menubar) + def onSetWavelength(self, event=None): + WavelengthDialog(self, self.wavelength, self.set_wavelength).Show() + + + def onReadPONI(self, event=None): sfile = FileOpen(self, 'Read PONI (pyFAI) calibration file', default_file='XRD.poni', @@ -223,7 +293,7 @@ def onReadXY(self, event=None): wildcard=XYWcards) if sfile is not None: top, xfile = os.path.split(sfile) - dxrd = xrd1d(file=sfile) + dxrd = xrd1d(file=sfile, wavelength=self.wavelength) self.add_data(dxrd, label=xfile) def onCIFBrowse(self, event=None): @@ -244,8 +314,8 @@ def onLoadCIF(self, cif=None): if cif is None: return t0 = time.time() - energy = E_from_lambda(self.wavelength) + energy = E_from_lambda(self.wavelength) sfact = cif.get_structure_factors(wavelength=self.wavelength) try: @@ -394,9 +464,9 @@ def Btn(msg, x, act): increment=0.01, min_val=0.001, max_val=5, action=self.on_bkg) wids['bkg_nsmooth'] = FloatSpin(panel, value=30, size=(90, -1), - digits=0, min_val=2, max_val=200, action=self.on_bkg) + digits=0, min_val=2, max_val=100, action=self.on_bkg) wids['bkg_porder'] = FloatSpin(panel, value=40, size=(90, -1), - digits=0, min_val=2, max_val=200, action=self.on_bkg) + digits=0, min_val=2, max_val=100, action=self.on_bkg) def CopyBtn(name): return Button(panel, 'Copy to Seleceted', size=(150, -1), @@ -477,9 +547,12 @@ def slabel(txt): self.Show() self.Raise() - def set_wavelength(self, w): - self.wids['wavelength'].SetLabel("%.6f" % w) - self.wids['energy_ev'].SetLabel("%.1f" % (PLANCK_HC/w)) + def set_wavelength(self, value): + self.wavelength = value + self.wids['wavelength'].SetLabel("%.6f" % value) + self.wids['energy_ev'].SetLabel("%.1f" % (PLANCK_HC/value)) + for key, dset in self.datasets.items(): + dset.set_wavelength(value) def onCopyAttr(self, name=None, event=None): # print("Copy ", name, event) @@ -641,13 +714,13 @@ def remove_selected_datasets(self, event=None): dlg.Destroy() if res.ok: - all_names = self.filelist.GetItems() + all = self.filelist.GetItems() for dname in sel: self.datasets.pop(dname) - all_fnames.remove(dname) + all.remove(dname) filelist.Clear() - for name in all_names: + for name in all: filelist.Append(name) def get_display(self, win=1, stacked=False): @@ -658,28 +731,31 @@ def get_display(self, win=1, stacked=False): def plot_dset(self, dset, plottype, newplot=True): win = int(self.wids['plot_win'].GetStringSelection()) xscale = self.wids['xscale'].GetSelection() - xlabel = self.wids['xscale'].GetStringSelection() + opts = {'show_legend': True, 'xmax': None, + 'xlabel': self.wids['xscale'].GetStringSelection(), + 'ylabel':'Scaled Intensity', + 'label': dset.label} + xdat = dset.q if xscale == 2: xdat = dset.d + opts['xmax'] = min(12.0, max(xdat)) elif xscale == 1: xdat = dset.twth ydat = 1.0*dset.I/dset.scale - ylabel = 'Scaled Intensity' if plottype == 'sub': ydat = 1.0*(dset.I-dset.bkgd)/dset.scale - ylabel = 'Scaled (Intensity - Background)' + opts['ylabel'] = 'Scaled (Intensity - Background)' pframe = self.get_display(win=win) plot = pframe.plot if newplot else pframe.oplot - plot(xdat, ydat, xlabel=xlabel, ylabel=ylabel, - label=dset.label, show_legend=True) + plot(xdat, ydat, **opts) if plottype == 'raw+bkg': y2dat = 1.0*dset.bkgd/dset.scale - ylabel = 'Scaled Intensity with Background' - pframe.oplot(xdat, y2dat, xlabel=xlabel, ylabel=ylabel, - label='background', show_legend=True) + opts['ylabel'] = 'Scaled Intensity with Background' + opts['label'] = 'background' + pframe.oplot(xdat, y2dat, **opts) def onPlotOne(self, event=None, label=None): if label is None: From 7136ded3a93575f3247b763d2fc10a5281c7f75e Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 6 Aug 2023 20:13:15 -0500 Subject: [PATCH 11/13] some cleanup for xdi2NXxas --- larch/io/nexus_xas.py | 57 ++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/larch/io/nexus_xas.py b/larch/io/nexus_xas.py index f0f8ad0a7..61af754d7 100644 --- a/larch/io/nexus_xas.py +++ b/larch/io/nexus_xas.py @@ -3,8 +3,8 @@ from larch.io import read_xdi from larch.utils.strutils import bytes2str from larch.math.utils import safe_log -from larch.utils.physical_constants import STD_LATTICE_CONSTANTS - +from larch.utils.physical_constants import (STD_LATTICE_CONSTANTS, + DEG2RAD, PLANCK_HC) NXXAS_URL = 'https://download.nexusformat.org/doc/html/classes/applications/NXxas.html' def parse_mono_reflection(refl): @@ -16,6 +16,14 @@ def parse_mono_reflection(refl): return tuple([int(r) for r in refl.split()]) +NXXAS_LINKS = {'element': 'scan/xrayedge/element', + 'edge': 'scan/xrayedge/edge', + 'energy': 'instrument/monochromator/energy', + 'rawdata': 'scan/data', + 'column_labels': '/scan/column_labels', + 'i0': 'instrument/i0/data'} + + def xdi2NXxas(xdidata, h5root, name='entry', compress=None): """add XDI data to an NXxas group in a NeXuS HDF5 file: @@ -95,16 +103,6 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): imono = instrument.create_group('monochromator') imono.attrs['NX_class'] = 'NXmonochromator' - ien = xdidata.array_labels.index('energy') - en_units = 'unknown' - if ien > -1: - en_units = xdidata.array_units[ien] - s = imono.create_dataset('energy', data=xdidata.energy, **compress) - s.attrs['units'] = en_units - if hasattr(xdidata, 'angle'): - s = imono.create_dataset('angle', data=xdidata.angle, **compress) - s.attrs['units'] = 'degrees' - # instrument/mono/crystal imonoxtal = imono.create_group('crystal') imonoxtal.attrs['NX_class'] = 'NXcrystal' @@ -125,6 +123,23 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): else: mono_dspacing = float(mono_dspacing) + if not hasattr(xdidata, 'energy') and hasattr(xdidata, 'angle'): + omega = PLANCK_HC/(2*mono_dspacing) + xdidata.energy = omega/np.sin(xdidata.angle*DEG2RAD) + en_units = 'eV' + else: + ien = xdidata.array_labels.index('energy') + en_units = 'eV' + if ien > -1: + en_units = xdidata.array_units[ien] + + s = imono.create_dataset('energy', data=xdidata.energy, **compress) + s.attrs['units'] = en_units + if hasattr(xdidata, 'angle'): + s = imono.create_dataset('angle', data=xdidata.angle, **compress) + s.attrs['units'] = 'degrees' + + imonoxtal.create_dataset('chemical_formula', data=mono_chem) imonoxtal.create_dataset('reflection', data=mono_refl) s = imonoxtal.create_dataset('d_spacing', data=mono_dspacing) @@ -189,7 +204,8 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): scan = xas.create_group('scan') scan.attrs['NX_class'] = 'NXscan' for key, val in xdi_scan.items(): - sample.create_dataset(key, data=val) + scan.create_dataset(key, data=val) + ncol, nrow = xdidata.data.shape scan.create_dataset('nP', data=nrow) scan.create_dataset('nCol', data=ncol) @@ -210,32 +226,29 @@ def xdi2NXxas(xdidata, h5root, name='entry', compress=None): if not hasattr(xdidata, 'itrans') and hasattr(xdidata, 'ifluor'): mode = 'Fluorescence' dat.create_dataset('mode', data=mode) - dat['element'] = h5py.SoftLink(f'/{entry_name}/scan/xrayedge/element') - dat['edge'] = h5py.SoftLink(f'/{entry_name}/scan/xrayedge/edge') - dat['column_labels'] = h5py.SoftLink(f'/{entry_name}/scan/column_labels') - # dat['rawdata'] = h5py.SoftLink(f'/{entry_name}/scan/data') + slinks = {k: v for k,v in NXXAS_LINKS.items()} - dat['energy'] = h5py.SoftLink(f'/{entry_name}/instrument/monochromator/energy') - dat['i0'] = h5py.SoftLink(f'/{entry_name}/instrument/i0/data') if hasattr(xdidata, 'itrans'): - dat['itrans'] = h5py.SoftLink(f'/{entry_name}/instrument/itrans/data') + slinks['itrans'] = 'instrument/itrans/data' mutrans = -safe_log(xdidata.itrans/xdidata.i0) dat.create_dataset('mutrans', data=mutrans, **compress) if hasattr(xdidata, 'ifluor'): - dat['ifluor'] = h5py.SoftLink(f'/{entry_name}/instrument/ifluor/data') + slink['ifluor'] = 'instrument/ifluor/data' mufluor = xdidata.ifluor/xdidata.i0 dat.create_dataset('mufluor', data=mufluor, **compress) if hasattr(xdidata, 'irefer'): - dat['irefer'] = h5py.SoftLink(f'/{entry_name}/instrument/irefer/data') + slink['irefer'] = 'instrument/irefer/data' if refmode.startswith('Fluo'): muref = xdidata.irefer/xdidata.i0 else: muref = -safe_log(xdidata.irefer/xdidata.itrans) dat.create_dataset('murefer', data=murefer, **compress) + for dest, source in slinks.items(): + dat[dest] = h5py.SoftLink(f'/{entry_name}/{source}') class NXxasFile(object): """ From 40e75b0db32b52a6d98e97e0446a054ed0568e7c Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 6 Aug 2023 20:14:55 -0500 Subject: [PATCH 12/13] add back larch_xrd1d as independent app, some renaming to larch_xrd1d, larch_xrf, etc --- larch/apps.py | 22 +++++++++++----------- setup.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/larch/apps.py b/larch/apps.py index d03df6e2c..77c35d280 100644 --- a/larch/apps.py +++ b/larch/apps.py @@ -75,15 +75,16 @@ def create_shortcut(self): make_shortcut(script, name=self.name, icon=icon, terminal=self.terminal, folder='Larch') -APPS = (LarchApp('Larch CLI', 'larch', terminal=True), - LarchApp('Larch GUI', 'larch --wxgui'), - LarchApp('XAS Viewer', 'xas_viewer', icon='onecone'), +APPS = (LarchApp('Larch CLI', 'larch', terminal=True), + LarchApp('Larch GUI', 'larch --wxgui'), + LarchApp('XAS Viewer', 'xas_viewer', icon='onecone'), + LarchApp('Larix', 'xas_viewer', icon='onecone'), LarchApp('GSE MapViewer', 'gse_mapviewer', icon='gse_xrfmap'), - LarchApp('XRF Display', 'xrfdisplay', icon='ptable'), + LarchApp('XRF Viewer', 'larch_xrf', icon='ptable'), + LarchApp('XRD1D Viewer', 'larch_xrd1d') # LarchApp('GSE DTCorrect', 'gse_dtcorrect'), # LarchApp('Dioptas', 'dioptas_larch', icon='dioptas'), # LarchApp('2D XRD Viewer', 'xrd2d_viewer'), - # LarchApp('1D XRD Viewer', 'xrd1d_viewer') ) @@ -99,7 +100,6 @@ def make_desktop_shortcuts(): updater.create_shortcut() - def make_cli(description='run larch program', filedesc='data file'): usage = "usage: %prog [options] file" parser = ArgumentParser(description=description) @@ -140,7 +140,7 @@ def run_xas_viewer(): kwargs = make_cli(description="Larch's XAS Viewer and Analysis Program") XASViewer(check_version=True, **kwargs).MainLoop() -def run_xrfdisplay(): +def run_larch_xrf(): """ XRF Display""" set_locale() use_mpl_wxagg() @@ -151,7 +151,7 @@ def run_xrfdisplay(): filedesc='MCA File (.mca)') XRFApp(**kwargs).MainLoop() -def run_xrfdisplay_epics(): +def run_epics_xrf(): """XRF Display for Epics Detectors""" set_locale() use_mpl_wxagg() @@ -160,12 +160,12 @@ def run_xrfdisplay_epics(): from larch.epics import EpicsXRFApp EpicsXRFApp().MainLoop() -def run_xrd1d_viewer(): +def run_larch_xrd1d(): """XRD Display for 1D patternss""" set_locale() use_mpl_wxagg() - from larch.wxxrd import XRD1DViewer - XRD1DViewer().MainLoop() + from larch.wxxrd import XRD1DApp + XRD1DApp().MainLoop() def run_xrd2d_viewer(): """XRD Display for 2D patternss""" diff --git a/setup.py b/setup.py index 69a89fb6d..91cb4f6a5 100644 --- a/setup.py +++ b/setup.py @@ -107,7 +107,7 @@ # list of top level scripts to add to Python's bin/ scripts = ['larch', 'larch_server', 'feff6l', 'feff8l', 'xas_viewer', - 'gse_mapviewer', 'xrfdisplay', 'xrfdisplay_epics'] + 'gse_mapviewer', 'larch_xrf', 'epics_xrf', 'larch_xrd1d'] larch_apps = ['{0:s} = larch.apps:run_{0:s}'.format(n) for n in scripts] From 5c12a83607aba40be02bf8fb8a353bfa2ea592c5 Mon Sep 17 00:00:00 2001 From: Matthew Newville Date: Sun, 6 Aug 2023 20:16:19 -0500 Subject: [PATCH 13/13] XDI references --- doc/larch.bib | 63 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 19 deletions(-) diff --git a/doc/larch.bib b/doc/larch.bib index 10d018103..b6f8a8912 100644 --- a/doc/larch.bib +++ b/doc/larch.bib @@ -1,4 +1,3 @@ - @string{jacs = "Journal of the American Chemical Society"} @string{jsr = "Journal of Synchrotron Radiation"} @string{prb = "Physical Review B"} @@ -18,6 +17,31 @@ @string{xbible Techniques of {EXAFS}, {SEXAFS}, and {XANES}}"} +@article{XDI2016, + author = {B. Ravel and M. Newville}, + title = {XAFS Data Interchange: A single spectrum XAFS data file format }, + journal = {Journal of Physics: Conference Series}, + year = "2016", + volume = "712", + pages = "012148", + doi = {10.1088/1742-6596/712/1/012148}, +} + + +@article{XASDataFormats, + author = {Ravel, B. and Hester, J. R. and Sole, V. A. and Newville, M.}, + title = {Towards data format standardization for X-ray absorption spectroscopy}, + journal = jsr, + year = {2012}, + volume = {19}, + number = {6}, + pages = {869-874}, + month = {Nov}, + note = {International Workshop on Improving Data Quality and Quantity for XAFS + Experiments, Tsukuba, JAPAN, APR 12-13, 2011}, + doi= {10.1107/S0909049512036886}, +} + @article{larch2013, author = {M. Newville}, @@ -39,7 +63,8 @@ @Misc{ixasportal title = {International X-ray Absorption Society}, key = {IXAS}, year = 2012, - url = {{https://www.ixasportal.net/}}} + url = {{https://xrayabsorption.org}}} + @article{ATOMS, author = {B. Ravel}, @@ -723,12 +748,12 @@ @article{Hall:es0164 } @InBook{HallWestbrook, - author = {Hall, S. R. and Westbrook, J. D.}, - title = {International Tables for Crystallography}, - chapter = {2.2}, - publisher = {IUCr}, - year = 2005, - volume = {G} + author = {Hall, S. R. and Westbrook, J. D.}, + title = {International Tables for Crystallography}, + chapter = {2.2}, + publisher = {IUCr}, + year = 2005, + volume = {G} } @@ -760,35 +785,35 @@ @article{PhysRevLett.27.1204 @Misc{data_format_working_group, key = "XAS Data Format Working Group", - title = {Data Format Working Group Wiki}, + title = {Data Format Working Group Wiki}, url = {{https://github.com/XraySpectroscopy/Data-Format-Working-Group/wiki}}, - year = 2012} + year = 2012} @Misc{hdf_group, key = {HDF GROUP}, title = {HDF GROUP - HDF5}, url = {{https://www.hdfgroup.org/HDF5/}}, - year = 2012} + year = 2012} @Misc{sqlite, - author = {Hipp, D. R.}, - title = {SQLite}, + author = {Hipp, D. R.}, + title = {SQLite}, url = {{https://www.sqlite.org}}, - year = 2012} + year = 2012} @Misc{xasformat_mailing_list, key = {XAS Data Format Working Group}, - title = "XASFORMAT mailing list", + title = "XASFORMAT mailing list", url = {{https://millenia.cars.aps.anl.gov/mailman/listinfo/xasformat}}, - year = 2012} + year = 2012} @Misc{lytle_database, - author = {Lytle, F. and Boyanov, B. and Segre, S.}, - title = {Farrel Lytle XAFS Database}, + author = {Lytle, F. and Boyanov, B. and Segre, S.}, + title = {Farrel Lytle XAFS Database}, url = {{https://ixs.iit.edu/database}}, - year = 1995 + year = 1995 }