From 2c6409ffb2dd2b38c912cea839fc3d4f61807727 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Mon, 18 Nov 2024 14:34:13 -0500 Subject: [PATCH 01/13] eta-omega maps file --- hexrd/config/config.py | 11 +++++ hexrd/config/findorientations.py | 47 ++++++++++++++++---- hexrd/findorientations.py | 75 ++++++++++++-------------------- 3 files changed, 77 insertions(+), 56 deletions(-) diff --git a/hexrd/config/config.py b/hexrd/config/config.py index 5e7aeaf47..72b8b04c4 100644 --- a/hexrd/config/config.py +++ b/hexrd/config/config.py @@ -8,12 +8,23 @@ logger = logging.getLogger('hexrd.config') class Config(object): + """Access a level of the YAML configuration file + PARAMETERS + ---------- + cfg: Config instance or a (pyyaml) YAMLObject + config representings a level of the YAML input + """ _dirty = False def __init__(self, cfg): self._cfg = cfg + @property + def parent(self): + """Parent Config file or None for root (top) level""" + return self._cfg if isinstance(self._cfg, type(self)) else None + @property def dirty(self): return self._dirty diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index c66034bc8..084bc9b2a 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -1,5 +1,5 @@ import os - +from pathlib import Path import logging import numpy as np @@ -229,14 +229,43 @@ def eta_step(self): 'find_orientations:orientation_maps:eta_step', default=0.25 ) - @property - def file(self): - temp = self._cfg.get('find_orientations:orientation_maps:file', - default=None) - if temp is not None: - if not os.path.isabs(temp): - temp = os.path.join(self._cfg.working_dir, temp) - return temp + def file(self, to_load): + """Path of eta-omega maps file + + This function implements the newer file placement for the eta-omega + maps file in the analysis directory, instead of the old placement in + the working directory. New files will be saved using the new + placement. For loading existing files, the new placement will be + checked first, and then the old placement. This ensures that old + data sets will still find existing map files. + + PARAMETERS + ---------- + to_load: bool + if True, the filename with the eta-omega maps; otherwise, it + returns the filename to save the eta-omega maps + + RETURNS + ------- + Path: + the path to an existing file or where to write a new file or None + """ + temp = Path(self._cfg.get( + 'find_orientations:orientation_maps:file', + default="eta-ome_maps.npz" + )) + if temp.suffix != ".npz": + temp = temp.with_suffix(".npz") + + oem_path = oem_new = self.parent.analysis_dir / temp + if temp.is_absolute(): + oem_path = temp + else: + ome_old = self.parent.working_dir / temp + if loading and not ome_new.exists(): + oem_path = ome_old if oeme_old.exists() else None + + return oem_path @property def threshold(self): diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 316773c52..03334b912 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -388,56 +388,48 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): Parameters ---------- - cfg : TYPE - DESCRIPTION. - pd : TYPE - DESCRIPTION. - image_series : TYPE - DESCRIPTION. - hkls : TYPE, optional - DESCRIPTION. The default is None. - clean : TYPE, optional - DESCRIPTION. The default is False. + cfg: Config instance + root config file for this problem + pd: PlaneData instance + crystallographic properties + image_series: ImageSeries instance + stack of images + hkls: list, default = None + list of HKLs used in the eta-omega maps. + clean: bool, default = False + flag indicating whether (if True) to overwrite existing maps file Returns ------- - TYPE - DESCRIPTION. + list: + list of eta-omega map arrays """ - # check maps filename - if cfg.find_orientations.orientation_maps.file is None: - maps_fname = '_'.join([cfg.analysis_id, "eta-ome_maps.npz"]) + fn = cfg.find_orientations.orientation_maps.file(to_load := True) + if clean: + logger.info( + 'clean option specified; recomputing eta/ome orientation maps' + ) + res = generate_eta_ome_maps(cfg, hkls=hkls) else: - maps_fname = cfg.find_orientations.orientation_maps.file - - fn = os.path.join(cfg.working_dir, maps_fname) - - # ???: necessary? - if fn.split('.')[-1] != 'npz': - fn = fn + '.npz' - - if not clean: try: res = EtaOmeMaps(fn) pd = res.planeData - logger.info('loaded eta/ome orientation maps from %s', fn) + logger.info(f'loaded eta/ome orientation maps from {fn}') shkls = pd.getHKLs(*res.iHKLList, asStr=True) logger.info( 'hkls used to generate orientation maps: %s', [f'[{i}]' for i in shkls] ) except (AttributeError, IOError): - logger.info("specified maps file '%s' not found " - + "and clean option specified; " - + "recomputing eta/ome orientation maps", - fn) + logger.info( + f"specified maps file '{fn}' not found " + f"and clean option not specified; " + f"recomputing eta/ome orientation maps" + ) res = generate_eta_ome_maps(cfg, hkls=hkls) - else: - logger.info('clean option specified; ' - + 'recomputing eta/ome orientation maps') - res = generate_eta_ome_maps(cfg, hkls=hkls) - filter_maps_if_requested(res, cfg) + + filter_maps_if_requested(res, cfg) return res @@ -571,21 +563,10 @@ def generate_eta_ome_maps(cfg, hkls=None, save=True): if save: # save maps - # ???: should perhaps set default maps name at module level - map_fname = cfg.find_orientations.orientation_maps.file \ - or '_'.join([cfg.analysis_id, "eta-ome_maps.npz"]) - - if not os.path.exists(cfg.working_dir): - os.mkdir(cfg.working_dir) - - fn = os.path.join( - cfg.working_dir, - map_fname - ) - + map_fname = cfg.find_orientations.orientation_maps.file(False) eta_ome.save(fn) - logger.info('saved eta/ome orientation maps to "%s"', fn) + logger.info(f'saved eta/ome orientation maps to "{fn}"', fn) return eta_ome From cea3172985c1662ac1cf590d371d38938630244c Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 19 Nov 2024 12:58:35 -0500 Subject: [PATCH 02/13] checkpoint: working with new logfile and maps file names, but failing on scored_orientations file --- hexrd/cli/find_orientations.py | 7 +-- hexrd/config/config.py | 2 +- hexrd/config/findorientations.py | 31 +++++++++--- hexrd/config/root.py | 86 +++++++++++++++++++------------- hexrd/findorientations.py | 4 +- 5 files changed, 79 insertions(+), 51 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index ca3f4c11a..2078dc89a 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -134,14 +134,9 @@ def execute(args, parser): quats_f ) sys.exit() - if not os.path.exists(cfg.working_dir): - os.makedirs(cfg.working_dir) # configure logging to file - logfile = os.path.join( - cfg.working_dir, - 'find-orientations_%s.log' % cfg.analysis_id - ) + logfile = cfg.find_orientations.logfile fh = logging.FileHandler(logfile, mode='w') fh.setLevel(log_level) fh.setFormatter( diff --git a/hexrd/config/config.py b/hexrd/config/config.py index 72b8b04c4..1b5a3683a 100644 --- a/hexrd/config/config.py +++ b/hexrd/config/config.py @@ -23,7 +23,7 @@ def __init__(self, cfg): @property def parent(self): """Parent Config file or None for root (top) level""" - return self._cfg if isinstance(self._cfg, type(self)) else None + return self._cfg if isinstance(self._cfg, Config) else None @property def dirty(self): diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index 084bc9b2a..a43b004aa 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -23,6 +23,14 @@ class FindOrientationsConfig(Config): + _find_ori = "find-orientations" + + @property + def logfile(self): + """Name of log file""" + actmat = self.parent.material.active.strip().replace(' ', '-') + return self.parent.analysis_dir() / f"{self._find_ori}-{actmat}.log" + # Subsections @property def orientation_maps(self): @@ -250,20 +258,31 @@ def file(self, to_load): Path: the path to an existing file or where to write a new file or None """ - temp = Path(self._cfg.get( + # + # Get file name. Because users often set file to "null", which + # returns a valid value of None, we have to check twice before setting + # it to the default value. + # + dflt = "eta-ome_maps.npz" + temp = self._cfg.get( 'find_orientations:orientation_maps:file', - default="eta-ome_maps.npz" - )) + default=dflt + ) + if temp is None: + temp = dflt + temp = Path(temp) + + # Now, we put the file in the analysis directory. if temp.suffix != ".npz": temp = temp.with_suffix(".npz") - oem_path = oem_new = self.parent.analysis_dir / temp + oem_path = ome_new = self.parent.analysis_dir() / temp if temp.is_absolute(): oem_path = temp else: ome_old = self.parent.working_dir / temp - if loading and not ome_new.exists(): - oem_path = ome_old if oeme_old.exists() else None + if to_load and not ome_new.exists(): + oem_path = ome_old if ome_old.exists() else None return oem_path diff --git a/hexrd/config/root.py b/hexrd/config/root.py index 18a7a86b1..07e564fd7 100644 --- a/hexrd/config/root.py +++ b/hexrd/config/root.py @@ -1,4 +1,5 @@ import os +from pathlib import Path import logging import multiprocessing as mp @@ -16,17 +17,64 @@ class RootConfig(Config): + @property + def working_dir(self): + """Working directory, either specified in file or current directory + + This directory is certain to exist. If the specified directory does + not exist, it defaults to the current working directory. + """ + try: + temp = Path(self.get('working_dir')) + if not temp.exists(): + raise IOError(f'"working_dir": {temp} does not exist') + return temp + + except RuntimeError: + temp = Path.cwd() + self.working_dir = temp + logger.info( + '"working_dir" not specified, defaulting to "%s"' % temp + ) + return temp + + @working_dir.setter + def working_dir(self, val): + val = os.path.abspath(val) + if not os.path.isdir(val): + raise IOError('"working_dir": "%s" does not exist' % val) + self.set('working_dir', val) + @property def analysis_name(self): + """Name of the analysis + + This will be used to set up the output directory. The name can + contain slash ("/") characters, which will generate a subdirectory + structure in the `analysis_dir`. + """ return str(self.get('analysis_name', default='analysis')) @analysis_name.setter def analysis_name(self, val): self.set('analysis_name', val) - @property def analysis_dir(self): - return os.path.join(self.working_dir, self.analysis_name) + """Analysis directory, where output files go + + The name is derived from `working_dir` and `analysis_name`. + Intermediate directories will be created. + """ + adir = Path(self.working_dir) / self.analysis_name + Path.mkdir(adir, parents=True, exist_ok=True) + return adir + + @property + def analysis_id(self): + return '_'.join( + [self.analysis_name.strip().replace(' ', '-'), + self.material.active.strip().replace(' ', '-')] + ) @property def find_orientations(self): @@ -67,13 +115,6 @@ def material(self): def material(self, material_config): self._material_config = material_config - @property - def analysis_id(self): - return '_'.join( - [self.analysis_name.strip().replace(' ', '-'), - self.material.active.strip().replace(' ', '-')] - ) - @property def multiprocessing(self): # determine number of processes to run in parallel @@ -126,33 +167,6 @@ def multiprocessing(self, val): % (mp.cpu_count(), val) ) - @property - def working_dir(self): - try: - temp = self.get('working_dir') - if not os.path.exists(temp): - raise IOError( - '"working_dir": "%s" does not exist', temp - ) - return temp - except RuntimeError: - temp = os.getcwd() - was_dirty = self.dirty - self.working_dir = temp - if not was_dirty: - self._dirty = False - logger.info( - '"working_dir" not specified, defaulting to "%s"' % temp - ) - return temp - - @working_dir.setter - def working_dir(self, val): - val = os.path.abspath(val) - if not os.path.isdir(val): - raise IOError('"working_dir": "%s" does not exist' % val) - self.set('working_dir', val) - @property def image_series(self): """Return the imageseries dictionary.""" diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 03334b912..27e67d435 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -413,7 +413,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): res = generate_eta_ome_maps(cfg, hkls=hkls) else: try: - res = EtaOmeMaps(fn) + res = EtaOmeMaps(str(fn)) pd = res.planeData logger.info(f'loaded eta/ome orientation maps from {fn}') shkls = pd.getHKLs(*res.iHKLList, asStr=True) @@ -563,7 +563,7 @@ def generate_eta_ome_maps(cfg, hkls=None, save=True): if save: # save maps - map_fname = cfg.find_orientations.orientation_maps.file(False) + fn = cfg.find_orientations.orientation_maps.file(False) eta_ome.save(fn) logger.info(f'saved eta/ome orientation maps to "{fn}"', fn) From 3ea78e9ce572ac0b351b51839c2d0602d1a40a89 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 19 Nov 2024 14:46:17 -0500 Subject: [PATCH 03/13] scored_orientations file is working --- hexrd/cli/find_orientations.py | 8 +------ hexrd/config/findorientations.py | 37 ++++++++++++++++++++++---------- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index 2078dc89a..5641d181f 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -51,14 +51,8 @@ def configure_parser(sub_parsers): def write_scored_orientations(results, cfg): - # grab working directory from config - wdir = cfg.working_dir - - scored_quats_filename = os.path.join( - wdir, '_'.join(['scored_orientations', cfg.analysis_id]) - ) np.savez_compressed( - scored_quats_filename, + cfg.find_orientations.orientation_maps.scored_orientations_file, **results['scored_orientations'] ) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index a43b004aa..88a582333 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -9,6 +9,7 @@ logger = logging.getLogger('hexrd.config') + # TODO: set these as defaults seed_search_methods = { 'label': dict(filter_radius=1, threshold=1), @@ -25,10 +26,13 @@ class FindOrientationsConfig(Config): _find_ori = "find-orientations" + def _active_material_str(self): + return self.parent.material.active.strip().replace(' ', '-') + @property def logfile(self): """Name of log file""" - actmat = self.parent.material.active.strip().replace(' ', '-') + actmat = self._active_material_str() return self.parent.analysis_dir() / f"{self._find_ori}-{actmat}.log" # Subsections @@ -263,29 +267,40 @@ def file(self, to_load): # returns a valid value of None, we have to check twice before setting # it to the default value. # - dflt = "eta-ome_maps.npz" + actmat = self.parent.find_orientations._active_material_str() + dflt = f"eta-ome-maps-{actmat}.npz" temp = self._cfg.get( 'find_orientations:orientation_maps:file', - default=dflt + default=None ) if temp is None: - temp = dflt - temp = Path(temp) + ome_new = self.parent.analysis_dir() / dflt + old_name = '_'.join([self.parent.analysis_id, "eta-ome_maps.npz"]) + ome_old = Path(self.parent.working_dir) / old_name + + else: + # User specified value + if temp.suffix != ".npz": + temp = temp.with_suffix(".npz") + ome_new = ome_old = temp # Now, we put the file in the analysis directory. - if temp.suffix != ".npz": - temp = temp.with_suffix(".npz") - oem_path = ome_new = self.parent.analysis_dir() / temp - if temp.is_absolute(): - oem_path = temp + if ome_new.is_absolute(): + oem_path = ome_new else: - ome_old = self.parent.working_dir / temp if to_load and not ome_new.exists(): oem_path = ome_old if ome_old.exists() else None return oem_path + @property + def scored_orientations_file(self): + root = self.parent + adir = root.analysis_dir() + actmat = root.find_orientations._active_material_str() + return Path(adir) / f'scored-orientations-{actmat}.npz' + @property def threshold(self): return self._cfg.get('find_orientations:orientation_maps:threshold') From e682fcd48833fe0d094f9867bb8e10520d4ef02c Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Fri, 22 Nov 2024 12:33:53 -0500 Subject: [PATCH 04/13] fit-grains works on NIST_Ruby/single_GE --- hexrd/cli/find_orientations.py | 36 +++++-------- hexrd/cli/fit_grains.py | 86 +++++++++++++------------------- hexrd/config/findorientations.py | 78 +++++++++++++++++++++++------ hexrd/config/fitgrains.py | 14 ++++++ hexrd/config/root.py | 13 +++-- hexrd/findorientations.py | 3 +- 6 files changed, 134 insertions(+), 96 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index 5641d181f..51f120a4c 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -50,33 +50,20 @@ def configure_parser(sub_parsers): p.set_defaults(func=execute) -def write_scored_orientations(results, cfg): +def write_results(results, cfg): + # Write scored orientations. np.savez_compressed( cfg.find_orientations.orientation_maps.scored_orientations_file, **results['scored_orientations'] ) - -def write_results(results, cfg): - # Write out the data - write_scored_orientations(results, cfg) - - # grab working directory from config - wdir = cfg.working_dir - - if not os.path.exists(cfg.analysis_dir): - os.makedirs(cfg.analysis_dir) - qbar_filename = os.path.join( - wdir, - 'accepted_orientations_' + cfg.analysis_id + '.dat' - ) + # Write accepted orientations. + qbar_filename = str(cfg.find_orientations.accepted_orientations_file()) np.savetxt(qbar_filename, results['qbar'].T, fmt='%.18e', delimiter='\t') - # ??? do we want to do this by default? - gw = instrument.GrainDataWriter( - os.path.join(cfg.analysis_dir, 'grains.out') - ) + # Write grains.out. + gw = instrument.GrainDataWriter(cfg.find_orientations.grains_file) for gid, q in enumerate(results['qbar'].T): phi = 2*np.arccos(q[0]) n = xfcapi.unit_vector(q[1:]) @@ -118,17 +105,18 @@ def execute(args, parser): cfg = config.open(args.yml)[0] # prepare the analysis directory - quats_f = os.path.join( - cfg.working_dir, - 'accepted_orientations_%s.dat' % cfg.analysis_id - ) - if os.path.exists(quats_f) and not (args.force or args.clean): + quats_f = cfg.find_orientations.accepted_orientations_file(to_load=True) + + if (quats_f is not None) and not (args.force or args.clean): logger.error( '%s already exists. Change yml file or specify "force" or "clean"', quats_f ) sys.exit() + # Create analysis directory and any intermediates. + cfg.analysis_dir.mkdir(parents=True, exist_ok=True) + # configure logging to file logfile = cfg.find_orientations.logfile fh = logging.FileHandler(logfile, mode='w') diff --git a/hexrd/cli/fit_grains.py b/hexrd/cli/fit_grains.py index 4122e49ab..8bc3861e7 100644 --- a/hexrd/cli/fit_grains.py +++ b/hexrd/cli/fit_grains.py @@ -8,6 +8,7 @@ from hexrd import config from hexrd import constants as cnst from hexrd import instrument +from hexrd.findorientations import find_orientations from hexrd.fitgrains import fit_grains from hexrd.transforms import xfcapi @@ -118,21 +119,12 @@ def write_results( instr = cfg.instrument.hedm nfit = len(fit_results) - # make output directories - if not os.path.exists(cfg.analysis_dir): - os.mkdir(cfg.analysis_dir) - for det_key in instr.detectors: - os.mkdir(os.path.join(cfg.analysis_dir, det_key)) - else: - # make sure panel dirs exist under analysis dir - for det_key in instr.detectors: - if not os.path.exists(os.path.join(cfg.analysis_dir, det_key)): - os.mkdir(os.path.join(cfg.analysis_dir, det_key)) - + # Make output directories: analysis directory and a subdirectory for + # each panel. + for det_key in instr.detectors: + (cfg.analysis_dir / det_key).mkdir(parents=True, exist_ok=True) - gw = instrument.GrainDataWriter( - os.path.join(cfg.analysis_dir, grains_filename) - ) + gw = instrument.GrainDataWriter(str(cfg.analysis_dir /grains_filename)) gd_array = np.zeros((nfit, 21)) gwa = instrument.GrainDataWriter(array=gd_array) for fit_result in fit_results: @@ -142,7 +134,7 @@ def write_results( gwa.close() gdata = GrainData.from_array(gd_array) - gdata.save(os.path.join(cfg.analysis_dir, grains_npz)) + gdata.save(str(cfg.analysis_dir / grains_npz)) def execute(args, parser): @@ -167,18 +159,14 @@ def execute(args, parser): cfg = cfgs[0] # use path to grains.out to determine if analysis exists - # !!! note that the analysis dir pre-pends the working_dir - grains_filename = os.path.join(cfg.analysis_dir, 'grains.out') + grains_filename = cfg.find_orientations.grains_file # path to accepted_orientations - quats_f = os.path.join( - cfg.working_dir, - 'accepted_orientations_%s.dat' % cfg.analysis_id - ) + quats_f = cfg.find_orientations.accepted_orientations_file(to_load = True) # some conditionals for arg handling - have_orientations = os.path.exists(quats_f) - existing_analysis = os.path.exists(grains_filename) + have_orientations = quats_f is not None + existing_analysis = grains_filename.exists() fit_estimate = cfg.fit_grains.estimate force_without_estimate = args.force and fit_estimate is None new_without_estimate = not existing_analysis and fit_estimate is None @@ -195,9 +183,9 @@ def execute(args, parser): raise(RuntimeError, "error loading indexing results '%s'" % quats_f) else: + quats_f = cfg.find_orientations.accepted_orientations_file() logger.info("Missing %s, running find-orientations", quats_f) logger.removeHandler(ch) - from hexrd.findorientations import find_orientations results = find_orientations(cfg) qbar = results['qbar'] logger.addHandler(ch) @@ -205,15 +193,26 @@ def execute(args, parser): logger.info('=== begin fit-grains ===') for cfg in cfgs: - # prepare the analysis directory - if os.path.exists(cfg.analysis_dir) and not clobber: + + # Check whether an existing analysis exists. + grains_filename = cfg.fit_grains.grains_file + + if grains_filename.exists() and not clobber: logger.error( - 'Analysis "%s" at %s already exists.' - ' Change yml file or specify "force"', - cfg.analysis_name, cfg.analysis_dir + 'Analysis "%s" already exists. ' + 'Change yml file or specify "force"', + cfg.analysis_name ) sys.exit() + # Set up analysis directory and output directories. + cfg.analysis_dir.mkdir(parents=True, exist_ok=True) + + instr = cfg.instrument.hedm + for det_key in instr.detectors: + det_dir = cfg.analysis_dir / det_key + det_dir.mkdir(exist_ok=True) + # Set HKLs to use. if cfg.fit_grains.reset_exclusions: excl_p = cfg.fit_grains.exclusion_parameters @@ -235,26 +234,10 @@ def execute(args, parser): ) logger.info(f'using {using_nhkls} HKLs') - # make output directories - instr = cfg.instrument.hedm - if not os.path.exists(cfg.analysis_dir): - os.makedirs(cfg.analysis_dir) - for det_key in instr.detectors: - os.mkdir(os.path.join(cfg.analysis_dir, det_key)) - else: - # make sure panel dirs exist under analysis dir - for det_key in instr.detectors: - if not os.path.exists(os.path.join(cfg.analysis_dir, det_key)): - os.mkdir(os.path.join(cfg.analysis_dir, det_key)) - logger.info('*** begin analysis "%s" ***', cfg.analysis_name) # configure logging to file for this particular analysis - logfile = os.path.join( - cfg.working_dir, - cfg.analysis_name, - 'fit-grains.log' - ) + logfile = cfg.fit_grains.logfile fh = logging.FileHandler(logfile, mode='w') fh.setLevel(log_level) ff = logging.Formatter( @@ -273,17 +256,15 @@ def execute(args, parser): pr = profile.Profile() pr.enable() - grains_filename = os.path.join( - cfg.analysis_dir, 'grains.out' - ) # some conditionals for arg handling - existing_analysis = os.path.exists(grains_filename) + existing_analysis = grains_filename.exists() fit_estimate = cfg.fit_grains.estimate new_with_estimate = not existing_analysis and fit_estimate is not None new_without_estimate = not existing_analysis and fit_estimate is None force_with_estimate = args.force and fit_estimate is not None force_without_estimate = args.force and fit_estimate is None + # # ------- handle args # - 'clean' indicates ignoring any estimate specified and starting with # the 'accepted_orientations' file. Will run find-orientations if @@ -292,6 +273,7 @@ def execute(args, parser): # option "fit_grains:estimate" is None, will use results from # find-orientations. If 'accepted_orientations' does not exists, # then it runs find-orientations. + # if args.clean or force_without_estimate or new_without_estimate: # need accepted orientations from indexing in this case if args.clean: @@ -304,6 +286,8 @@ def execute(args, parser): + "using default" ) try: + # Write the accepted orientations (in `qbar`) to the + # grains.out file gw = instrument.GrainDataWriter(grains_filename) for i_g, q in enumerate(qbar.T): phi = 2*np.arccos(q[0]) @@ -316,7 +300,7 @@ def execute(args, parser): except(IOError): raise(RuntimeError, "indexing results '%s' not found!" - % 'accepted_orientations_' + cfg.analysis_id + '.dat') + % str(grains_filename)) elif force_with_estimate or new_with_estimate: grains_filename = fit_estimate logger.info("using initial estimate '%s'", fit_estimate) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index 88a582333..8f8ce9fe2 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -33,7 +33,46 @@ def _active_material_str(self): def logfile(self): """Name of log file""" actmat = self._active_material_str() - return self.parent.analysis_dir() / f"{self._find_ori}-{actmat}.log" + return self.parent.analysis_dir / f"{self._find_ori}-{actmat}.log" + + def accepted_orientations_file(self, to_load=False): + """Path of accepted_orientations file + + PARAMETERS + ---------- + to_load: bool, default = False + if True, check whether possible file names exist + + RETURNS + ------- + Path or None: + if to_load is False, it returns the path to write the file; if + True, it checks for existing files (new name first) and returns + an existing file name or None + """ + actmat = self._active_material_str() + newname = f"accepted-orientations-{actmat}.dat" + aof_new = self.parent.analysis_dir / newname + + if not to_load: + fname = aof_new + else: + fname = None + if aof_new.exists(): + fname = aof_new + else: + oldname = ( + 'accepted_orientations_%s.dat' % self.parent.analysis_id + ) + aof_old = self.parent.working_dir / oldname + if aof_old.exists(): + fname = aof_old + + return fname + + @property + def grains_file(self): + return self.parent.analysis_dir / "grains.out" # Subsections @property @@ -241,7 +280,7 @@ def eta_step(self): 'find_orientations:orientation_maps:eta_step', default=0.25 ) - def file(self, to_load): + def file(self, to_load=False): """Path of eta-omega maps file This function implements the newer file placement for the eta-omega @@ -253,14 +292,15 @@ def file(self, to_load): PARAMETERS ---------- - to_load: bool - if True, the filename with the eta-omega maps; otherwise, it - returns the filename to save the eta-omega maps + to_load: bool, default = False + if True, check whether possible file names exist RETURNS ------- - Path: - the path to an existing file or where to write a new file or None + Path or None: + if `to_load` is False, it returns the path to write the file; if + True, it checks for existing files (new name first) and returns + the name of an existing file or None """ # # Get file name. Because users often set file to "null", which @@ -274,30 +314,38 @@ def file(self, to_load): default=None ) if temp is None: - ome_new = self.parent.analysis_dir() / dflt + ome_new = self.parent.analysis_dir / dflt old_name = '_'.join([self.parent.analysis_id, "eta-ome_maps.npz"]) ome_old = Path(self.parent.working_dir) / old_name else: - # User specified value + # User specified value. if temp.suffix != ".npz": temp = temp.with_suffix(".npz") ome_new = ome_old = temp - # Now, we put the file in the analysis directory. + # Now, we whether to use the old or the new and set the correct + # directory. if ome_new.is_absolute(): - oem_path = ome_new + ome_path = ome_new else: - if to_load and not ome_new.exists(): - oem_path = ome_old if ome_old.exists() else None + if not to_load: + ome_path = ome_new + else: + if ome_new.exists(): + ome_path = ome_new + elif ome_old.exists(): + ome_path = ome_old + else: + ome_path = None - return oem_path + return ome_path @property def scored_orientations_file(self): root = self.parent - adir = root.analysis_dir() + adir = root.analysis_dir actmat = root.find_orientations._active_material_str() return Path(adir) / f'scored-orientations-{actmat}.npz' diff --git a/hexrd/config/fitgrains.py b/hexrd/config/fitgrains.py index 2024d853a..8f94bd993 100644 --- a/hexrd/config/fitgrains.py +++ b/hexrd/config/fitgrains.py @@ -34,11 +34,25 @@ def tth(self): class FitGrainsConfig(Config): + _fit_grains = "fit-grains" + + def _active_material_str(self): + return self.parent.material.active.strip().replace(' ', '-') + def __init__(self, cfg): super().__init__(cfg) re, ep = get_exclusion_parameters(self._cfg, 'fit_grains') self._reset_exclusions, self._exclusion_parameters = re, ep + @property + def logfile(self): + """Name of log file""" + return self.parent.analysis_dir / f"fit-grains.log" + + @property + def grains_file(self): + return self.parent.analysis_dir / "grains.out" + @property def reset_exclusions(self): """Flag to use hkls saved in the material""" diff --git a/hexrd/config/root.py b/hexrd/config/root.py index 07e564fd7..3e6e090ae 100644 --- a/hexrd/config/root.py +++ b/hexrd/config/root.py @@ -47,7 +47,7 @@ def working_dir(self, val): @property def analysis_name(self): - """Name of the analysis + """Name (Path) of the analysis This will be used to set up the output directory. The name can contain slash ("/") characters, which will generate a subdirectory @@ -59,14 +59,17 @@ def analysis_name(self): def analysis_name(self, val): self.set('analysis_name', val) - def analysis_dir(self): + @property + def analysis_dir(self, mkdirs=False): """Analysis directory, where output files go - The name is derived from `working_dir` and `analysis_name`. - Intermediate directories will be created. + The name is derived from `working_dir` and `analysis_name`. This + propetry returns a Path object. The directory and any intermediate + directories can be created with the `mkdir()` method, e.g. + + >>> analysis_dir.mkdir(parents=True, exist_ok=True) """ adir = Path(self.working_dir) / self.analysis_name - Path.mkdir(adir, parents=True, exist_ok=True) return adir @property diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 27e67d435..7ce917686 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -563,7 +563,7 @@ def generate_eta_ome_maps(cfg, hkls=None, save=True): if save: # save maps - fn = cfg.find_orientations.orientation_maps.file(False) + fn = cfg.find_orientations.orientation_maps.file() eta_ome.save(fn) logger.info(f'saved eta/ome orientation maps to "{fn}"', fn) @@ -705,6 +705,7 @@ def find_orientations(cfg, """ # grab objects from config + cfg.analysis_dir.mkdir(parents=True, exist_ok=True) plane_data = cfg.material.plane_data imsd = cfg.image_series instr = cfg.instrument.hedm From 5a88225ec6b2830fd91ccdfc337f0f7334e969e4 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Sun, 24 Nov 2024 18:56:57 -0500 Subject: [PATCH 05/13] made module variables for hard-wired filenames --- hexrd/fitgrains.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hexrd/fitgrains.py b/hexrd/fitgrains.py index 61ac0cd7f..b3c476d85 100644 --- a/hexrd/fitgrains.py +++ b/hexrd/fitgrains.py @@ -19,6 +19,13 @@ logger = logging.getLogger(__name__) +# These are file names that were hardwired in the code. I am putting them +# here so they will be easier to find if we want to make them user inputs +# at some point later. + +OVERLAP_TABLE_FILE = 'overlap_table.npz' +SPOTS_OUT_FILE = "spots_%05d.out" + # multiprocessing fit funcs def fit_grain_FF_init(params): @@ -157,7 +164,7 @@ def fit_grain_FF_reduced(grain_id): ot = np.load( os.path.join( analysis_dirname, os.path.join( - det_key, 'overlap_table.npz' + det_key, OVERLAP_TABLE_FILE ) ) ) @@ -343,7 +350,7 @@ def fit_grains(cfg, if ids_to_refine is not None: grains_table = np.atleast_2d(grains_table[ids_to_refine, :]) - spots_filename = "spots_%05d.out" if write_spots_files else None + spots_filename = SPOTS_OUT_FILE if write_spots_files else None params = dict( grains_table=grains_table, plane_data=cfg.material.plane_data, From 171a05480fb3958b7507b333ea213bdd80e831ad Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Mon, 25 Nov 2024 14:45:39 -0500 Subject: [PATCH 06/13] added attribute to root config, and updated various filenames to refelect that --- hexrd/config/findorientations.py | 135 +++++++++++-------------------- hexrd/config/root.py | 17 +++- 2 files changed, 61 insertions(+), 91 deletions(-) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index 8f8ce9fe2..e7497c9ea 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -32,46 +32,32 @@ def _active_material_str(self): @property def logfile(self): """Name of log file""" - actmat = self._active_material_str() - return self.parent.analysis_dir / f"{self._find_ori}-{actmat}.log" - - def accepted_orientations_file(self, to_load=False): - """Path of accepted_orientations file - - PARAMETERS - ---------- - to_load: bool, default = False - if True, check whether possible file names exist - - RETURNS - ------- - Path or None: - if to_load is False, it returns the path to write the file; if - True, it checks for existing files (new name first) and returns - an existing file name or None - """ - actmat = self._active_material_str() - newname = f"accepted-orientations-{actmat}.dat" - aof_new = self.parent.analysis_dir / newname + if self.parent.new_file_placement: + fname = f"{self._find_ori}-{self._active_material_str()}.log" + pth = self.parent.analysis_dir / fname + else: + fname = f"{self._find_ori}_{cfg.analysis_id}.log" + pth = self.parent.working_dir / name - if not to_load: - fname = aof_new + return pth + + def accepted_orientations_file(self): + """Path of accepted_orientations file""" + actmat = self._active_material_str() + if self.parent.new_file_placement: + newname = f"accepted-orientations-{actmat}.dat" + aof_path = self.parent.analysis_dir / newname else: - fname = None - if aof_new.exists(): - fname = aof_new - else: - oldname = ( - 'accepted_orientations_%s.dat' % self.parent.analysis_id - ) - aof_old = self.parent.working_dir / oldname - if aof_old.exists(): - fname = aof_old + oldname = ( + 'accepted_orientations_%s.dat' % self.parent.analysis_id + ) + aof_path = self.parent.working_dir / oldname - return fname + return aof_path @property def grains_file(self): + """Path to `grains.out` file""" return self.parent.analysis_dir / "grains.out" # Subsections @@ -280,74 +266,49 @@ def eta_step(self): 'find_orientations:orientation_maps:eta_step', default=0.25 ) - def file(self, to_load=False): - """Path of eta-omega maps file - - This function implements the newer file placement for the eta-omega - maps file in the analysis directory, instead of the old placement in - the working directory. New files will be saved using the new - placement. For loading existing files, the new placement will be - checked first, and then the old placement. This ensures that old - data sets will still find existing map files. - - PARAMETERS - ---------- - to_load: bool, default = False - if True, check whether possible file names exist - - RETURNS - ------- - Path or None: - if `to_load` is False, it returns the path to write the file; if - True, it checks for existing files (new name first) and returns - the name of an existing file or None - """ + @property + def file(self): + """Path of eta-omega maps file""" # # Get file name. Because users often set file to "null", which # returns a valid value of None, we have to check twice before setting # it to the default value. # - actmat = self.parent.find_orientations._active_material_str() - dflt = f"eta-ome-maps-{actmat}.npz" + root = self.parent + if root.new_file_placement: + actmat = root.find_orientations._active_material_str() + dflt = f"eta-ome-maps-{actmat}.npz" + mapf = root.analysis_dir / dflt + else: + fname = '_'.join([root.analysis_id, "eta-ome_maps.npz"]) + mapf = root.working_dir / fname + + # Now check the YAML. temp = self._cfg.get( 'find_orientations:orientation_maps:file', default=None ) - if temp is None: - ome_new = self.parent.analysis_dir / dflt - old_name = '_'.join([self.parent.analysis_id, "eta-ome_maps.npz"]) - ome_old = Path(self.parent.working_dir) / old_name - - else: - # User specified value. - if temp.suffix != ".npz": - temp = temp.with_suffix(".npz") - ome_new = ome_old = temp - - # Now, we whether to use the old or the new and set the correct - # directory. - - if ome_new.is_absolute(): - ome_path = ome_new - else: - if not to_load: - ome_path = ome_new + if temp is not None: + ptemp = Path(temp) + if ptemp.is_absolute(): + mapf = ptemp else: - if ome_new.exists(): - ome_path = ome_new - elif ome_old.exists(): - ome_path = ome_old - else: - ome_path = None + mapf = root.working_dir / ptemp - return ome_path + return mapf @property def scored_orientations_file(self): root = self.parent - adir = root.analysis_dir - actmat = root.find_orientations._active_material_str() - return Path(adir) / f'scored-orientations-{actmat}.npz' + if root.new_file_placement: + adir = root.analysis_dir + actmat = root.find_orientations._active_material_str() + sof = Path(adir) / f'scored-orientations-{actmat}.npz' + else: + fname = '_'.join(['scored_orientations', cfg.analysis_id]) + sof = root.working_dir / fname + + return sof @property def threshold(self): diff --git a/hexrd/config/root.py b/hexrd/config/root.py index 3e6e090ae..900ebac42 100644 --- a/hexrd/config/root.py +++ b/hexrd/config/root.py @@ -40,9 +40,9 @@ def working_dir(self): @working_dir.setter def working_dir(self, val): - val = os.path.abspath(val) - if not os.path.isdir(val): - raise IOError('"working_dir": "%s" does not exist' % val) + val = Path(val) + if not val.is_dir(): + raise IOError('"working_dir": "%s" does not exist' % str(val)) self.set('working_dir', val) @property @@ -60,7 +60,7 @@ def analysis_name(self, val): self.set('analysis_name', val) @property - def analysis_dir(self, mkdirs=False): + def analysis_dir(self): """Analysis directory, where output files go The name is derived from `working_dir` and `analysis_name`. This @@ -79,6 +79,15 @@ def analysis_id(self): self.material.active.strip().replace(' ', '-')] ) + @property + def new_file_placement(self): + """Use new file placements for find-orientations and fit-grains + + The new file placement rules put several files in the `analysis_dir` + instead of the `work + """ + return self.get('new_file_placement', default=False) + @property def find_orientations(self): return FindOrientationsConfig(self) From 14c6e78234c4555145ea129429e150c8a7d6746b Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Mon, 25 Nov 2024 15:28:27 -0500 Subject: [PATCH 07/13] updated use of config properties for various filenames --- hexrd/cli/find_orientations.py | 4 ++-- hexrd/cli/fit_grains.py | 6 +++--- hexrd/findorientations.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index 51f120a4c..982be1799 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -58,7 +58,7 @@ def write_results(results, cfg): ) # Write accepted orientations. - qbar_filename = str(cfg.find_orientations.accepted_orientations_file()) + qbar_filename = str(cfg.find_orientations.accepted_orientations_file) np.savetxt(qbar_filename, results['qbar'].T, fmt='%.18e', delimiter='\t') @@ -105,7 +105,7 @@ def execute(args, parser): cfg = config.open(args.yml)[0] # prepare the analysis directory - quats_f = cfg.find_orientations.accepted_orientations_file(to_load=True) + quats_f = cfg.find_orientations.accepted_orientations_file if (quats_f is not None) and not (args.force or args.clean): logger.error( diff --git a/hexrd/cli/fit_grains.py b/hexrd/cli/fit_grains.py index 8bc3861e7..cd7c34b25 100644 --- a/hexrd/cli/fit_grains.py +++ b/hexrd/cli/fit_grains.py @@ -162,10 +162,10 @@ def execute(args, parser): grains_filename = cfg.find_orientations.grains_file # path to accepted_orientations - quats_f = cfg.find_orientations.accepted_orientations_file(to_load = True) + quats_f = cfg.find_orientations.accepted_orientations_file # some conditionals for arg handling - have_orientations = quats_f is not None + have_orientations = quats_f.exists() existing_analysis = grains_filename.exists() fit_estimate = cfg.fit_grains.estimate force_without_estimate = args.force and fit_estimate is None @@ -183,7 +183,7 @@ def execute(args, parser): raise(RuntimeError, "error loading indexing results '%s'" % quats_f) else: - quats_f = cfg.find_orientations.accepted_orientations_file() + quats_f = cfg.find_orientations.accepted_orientations_file logger.info("Missing %s, running find-orientations", quats_f) logger.removeHandler(ch) results = find_orientations(cfg) diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 7ce917686..86b01e08a 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -405,7 +405,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): list of eta-omega map arrays """ - fn = cfg.find_orientations.orientation_maps.file(to_load := True) + fn = cfg.find_orientations.orientation_maps.file if clean: logger.info( 'clean option specified; recomputing eta/ome orientation maps' @@ -415,7 +415,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): try: res = EtaOmeMaps(str(fn)) pd = res.planeData - logger.info(f'loaded eta/ome orientation maps from {fn}') + logger.info(f'loaded eta/ome orientation maps from {str(fn)}') shkls = pd.getHKLs(*res.iHKLList, asStr=True) logger.info( 'hkls used to generate orientation maps: %s', @@ -423,7 +423,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): ) except (AttributeError, IOError): logger.info( - f"specified maps file '{fn}' not found " + f"specified maps file '{str(fn)}' not found " f"and clean option not specified; " f"recomputing eta/ome orientation maps" ) From 5dc4745256d6809d041fd72c1f16216d050f1b84 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 26 Nov 2024 10:28:02 -0500 Subject: [PATCH 08/13] fixed paths in logfile --- hexrd/config/findorientations.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index e7497c9ea..4f506064e 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -32,15 +32,17 @@ def _active_material_str(self): @property def logfile(self): """Name of log file""" + root = self.parent if self.parent.new_file_placement: fname = f"{self._find_ori}-{self._active_material_str()}.log" - pth = self.parent.analysis_dir / fname + pth = root.analysis_dir / fname else: - fname = f"{self._find_ori}_{cfg.analysis_id}.log" - pth = self.parent.working_dir / name + fname = f"{self._find_ori}_{root.analysis_id}.log" + pth = self.parent.working_dir / fname return pth + @property def accepted_orientations_file(self): """Path of accepted_orientations file""" actmat = self._active_material_str() From 62c07b5427e68215c0dd241c52fbd33724de1c41 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 26 Nov 2024 11:43:15 -0500 Subject: [PATCH 09/13] minor fixes; find-orientations runs on singleGE/ruby_config.yml --- hexrd/cli/find_orientations.py | 2 +- hexrd/findorientations.py | 2 +- hexrd/fitgrains.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index 982be1799..4ff57f16d 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -107,7 +107,7 @@ def execute(args, parser): # prepare the analysis directory quats_f = cfg.find_orientations.accepted_orientations_file - if (quats_f is not None) and not (args.force or args.clean): + if (quats_f.exists()) and not (args.force or args.clean): logger.error( '%s already exists. Change yml file or specify "force" or "clean"', quats_f diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 86b01e08a..92685517c 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -563,7 +563,7 @@ def generate_eta_ome_maps(cfg, hkls=None, save=True): if save: # save maps - fn = cfg.find_orientations.orientation_maps.file() + fn = cfg.find_orientations.orientation_maps.file eta_ome.save(fn) logger.info(f'saved eta/ome orientation maps to "{fn}"', fn) diff --git a/hexrd/fitgrains.py b/hexrd/fitgrains.py index b3c476d85..5572233e6 100644 --- a/hexrd/fitgrains.py +++ b/hexrd/fitgrains.py @@ -365,7 +365,7 @@ def fit_grains(cfg, eta_ranges=eta_ranges, ome_period=ome_period, analysis_dirname=cfg.analysis_dir, - spots_filename=spots_filename) + spots_filename=SPOTS_OUT_FILE) # ===================================================================== # EXECUTE MP FIT From 69a74ebefd7e94332b40600e2d84d6f6edd59406 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Mon, 2 Dec 2024 15:32:28 -0500 Subject: [PATCH 10/13] one-line fixes --- hexrd/config/findorientations.py | 2 +- hexrd/config/material.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index 4f506064e..cd6b1effc 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -307,7 +307,7 @@ def scored_orientations_file(self): actmat = root.find_orientations._active_material_str() sof = Path(adir) / f'scored-orientations-{actmat}.npz' else: - fname = '_'.join(['scored_orientations', cfg.analysis_id]) + fname = '_'.join(['scored_orientations', root.analysis_id]) sof = root.working_dir / fname return sof diff --git a/hexrd/config/material.py b/hexrd/config/material.py index fc73dfa3a..c55a985c6 100644 --- a/hexrd/config/material.py +++ b/hexrd/config/material.py @@ -31,8 +31,8 @@ def definitions(self): if os.path.exists(temp): return temp raise IOError( - '"material:definitions": "%s" does not exist' - ) + f'"material:definitions": "{temp}" does not exist' + ) @property def active(self): From 3fa1d8c3fa2b174282698ec254b8b42bad6833cb Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 3 Dec 2024 14:24:24 -0500 Subject: [PATCH 11/13] fix log message --- hexrd/findorientations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 92685517c..3088a9d5d 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -566,7 +566,7 @@ def generate_eta_ome_maps(cfg, hkls=None, save=True): fn = cfg.find_orientations.orientation_maps.file eta_ome.save(fn) - logger.info(f'saved eta/ome orientation maps to "{fn}"', fn) + logger.info(f'saved eta/ome orientation maps to "{fn}"') return eta_ome From a4bf898c397e5590e3bcb75893402710d73c26a6 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Tue, 10 Dec 2024 16:27:39 -0500 Subject: [PATCH 12/13] fixes from pull request review --- hexrd/cli/find_orientations.py | 16 ++++++---------- hexrd/cli/fit_grains.py | 1 - hexrd/config/fitgrains.py | 4 +--- hexrd/config/root.py | 33 +++++++++++++++++---------------- hexrd/findorientations.py | 27 +++++++++++++++++++++++---- 5 files changed, 47 insertions(+), 34 deletions(-) diff --git a/hexrd/cli/find_orientations.py b/hexrd/cli/find_orientations.py index 4ff57f16d..7b3792b8d 100644 --- a/hexrd/cli/find_orientations.py +++ b/hexrd/cli/find_orientations.py @@ -1,11 +1,16 @@ from __future__ import print_function, division, absolute_import import os +import logging +import sys + import numpy as np from hexrd import constants as const +from hexrd import config from hexrd import instrument from hexrd.transforms import xfcapi +from hexrd.findorientations import find_orientations, write_scored_orientations descr = 'Process rotation image series to find grain orientations' @@ -52,10 +57,7 @@ def configure_parser(sub_parsers): def write_results(results, cfg): # Write scored orientations. - np.savez_compressed( - cfg.find_orientations.orientation_maps.scored_orientations_file, - **results['scored_orientations'] - ) + write_scored_orientations(results, cfg) # Write accepted orientations. qbar_filename = str(cfg.find_orientations.accepted_orientations_file) @@ -73,12 +75,6 @@ def write_results(results, cfg): def execute(args, parser): - import logging - import sys - - from hexrd import config - from hexrd.findorientations import find_orientations - # make sure hkls are passed in as a list of ints try: if args.hkls is not None: diff --git a/hexrd/cli/fit_grains.py b/hexrd/cli/fit_grains.py index cd7c34b25..e47629408 100644 --- a/hexrd/cli/fit_grains.py +++ b/hexrd/cli/fit_grains.py @@ -183,7 +183,6 @@ def execute(args, parser): raise(RuntimeError, "error loading indexing results '%s'" % quats_f) else: - quats_f = cfg.find_orientations.accepted_orientations_file logger.info("Missing %s, running find-orientations", quats_f) logger.removeHandler(ch) results = find_orientations(cfg) diff --git a/hexrd/config/fitgrains.py b/hexrd/config/fitgrains.py index 8f94bd993..8a708efec 100644 --- a/hexrd/config/fitgrains.py +++ b/hexrd/config/fitgrains.py @@ -34,8 +34,6 @@ def tth(self): class FitGrainsConfig(Config): - _fit_grains = "fit-grains" - def _active_material_str(self): return self.parent.material.active.strip().replace(' ', '-') @@ -47,7 +45,7 @@ def __init__(self, cfg): @property def logfile(self): """Name of log file""" - return self.parent.analysis_dir / f"fit-grains.log" + return self.parent.analysis_dir / "fit-grains.log" @property def grains_file(self): diff --git a/hexrd/config/root.py b/hexrd/config/root.py index 900ebac42..b8944b903 100644 --- a/hexrd/config/root.py +++ b/hexrd/config/root.py @@ -21,22 +21,23 @@ class RootConfig(Config): def working_dir(self): """Working directory, either specified in file or current directory - This directory is certain to exist. If the specified directory does - not exist, it defaults to the current working directory. + If the directory is not specified in the config file, then it will + default to the current working directory. If it is specified, the + director must exist, or it will throw and IOError. """ - try: - temp = Path(self.get('working_dir')) - if not temp.exists(): - raise IOError(f'"working_dir": {temp} does not exist') - return temp - - except RuntimeError: - temp = Path.cwd() - self.working_dir = temp + wdir = self.get('working_dir', default=None) + if wdir is not None: + wdir = Path(wdir) + if not wdir.exists(): + raise IOError(f'"working_dir": {str(wdir)} does not exist') + else: + # Working directory has not been specified, so we use current + # directory. + wdir = Path.cwd() logger.info( - '"working_dir" not specified, defaulting to "%s"' % temp - ) - return temp + '"working_dir" not specified, defaulting to "%s"' % wdir + ) + return wdir @working_dir.setter def working_dir(self, val): @@ -47,7 +48,7 @@ def working_dir(self, val): @property def analysis_name(self): - """Name (Path) of the analysis + """Name of the analysis This will be used to set up the output directory. The name can contain slash ("/") characters, which will generate a subdirectory @@ -64,7 +65,7 @@ def analysis_dir(self): """Analysis directory, where output files go The name is derived from `working_dir` and `analysis_name`. This - propetry returns a Path object. The directory and any intermediate + property returns a Path object. The directory and any intermediate directories can be created with the `mkdir()` method, e.g. >>> analysis_dir.mkdir(parents=True, exist_ok=True) diff --git a/hexrd/findorientations.py b/hexrd/findorientations.py index 3088a9d5d..04d5318c8 100755 --- a/hexrd/findorientations.py +++ b/hexrd/findorientations.py @@ -42,6 +42,22 @@ # ============================================================================= +def write_scored_orientations(results, fname): + """Write scored orientations to a file + + PARAMETERS + ---------- + results: dict + output of main `find_orientations` function + cfg: Config instance + the main Config input file for `find-orientations` + """ + np.savez_compressed( + cfg.find_orientations.orientation_maps.scored_orientations_file, + **results['scored_orientations'] + ) + + def _process_omegas(omegaimageseries_dict): """Extract omega period and ranges from an OmegaImageseries dictionary.""" oims = next(iter(omegaimageseries_dict.values())) @@ -384,7 +400,10 @@ def quat_distance(x, y): def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): """ - Load the eta-ome maps specified by the config and CLI flags. + Load the eta-ome maps specified by the config and CLI flags. If the + maps file exists, it will return those values. If the file does not exist, + it will generate them using the passed HKLs (if not None) or the HKLs + specified in the config file (if passed HKLs are Noe). Parameters ---------- @@ -395,7 +414,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): image_series: ImageSeries instance stack of images hkls: list, default = None - list of HKLs used in the eta-omega maps. + list of HKLs used to generate the eta-omega maps clean: bool, default = False flag indicating whether (if True) to overwrite existing maps file @@ -415,7 +434,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): try: res = EtaOmeMaps(str(fn)) pd = res.planeData - logger.info(f'loaded eta/ome orientation maps from {str(fn)}') + logger.info(f'loaded eta/ome orientation maps from {fn}') shkls = pd.getHKLs(*res.iHKLList, asStr=True) logger.info( 'hkls used to generate orientation maps: %s', @@ -429,7 +448,7 @@ def load_eta_ome_maps(cfg, pd, image_series, hkls=None, clean=False): ) res = generate_eta_ome_maps(cfg, hkls=hkls) - filter_maps_if_requested(res, cfg) + filter_maps_if_requested(res, cfg) return res From 53e18477cb924597e5ca2485c2cce15c6a339c73 Mon Sep 17 00:00:00 2001 From: "donald e. boyce" Date: Wed, 11 Dec 2024 11:23:28 -0500 Subject: [PATCH 13/13] fixing tests to match new file placement --- hexrd/config/findorientations.py | 4 +++- tests/config/test_find_orientations.py | 7 +++++-- tests/config/test_root.py | 12 ++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hexrd/config/findorientations.py b/hexrd/config/findorientations.py index cd6b1effc..6184f8539 100644 --- a/hexrd/config/findorientations.py +++ b/hexrd/config/findorientations.py @@ -290,7 +290,9 @@ def file(self): 'find_orientations:orientation_maps:file', default=None ) - if temp is not None: + if temp is None: + return None + else: ptemp = Path(temp) if ptemp.is_absolute(): mapf = ptemp diff --git a/tests/config/test_find_orientations.py b/tests/config/test_find_orientations.py index 8ea3abc6b..c51b245f5 100644 --- a/tests/config/test_find_orientations.py +++ b/tests/config/test_find_orientations.py @@ -9,6 +9,9 @@ """ analysis_name: analysis working_dir: %(tempdir)s +material: + definitions: %(existing_file)s + active: actmat --- find_orientations: orientation_maps: @@ -326,11 +329,11 @@ def test_file(self): self.cfgs[0].find_orientations.orientation_maps.file is None ) self.assertEqual( - self.cfgs[1].find_orientations.orientation_maps.file, + str(self.cfgs[1].find_orientations.orientation_maps.file), os.path.join(test_data['tempdir'], test_data['nonexistent_file']) ) self.assertEqual( - self.cfgs[2].find_orientations.orientation_maps.file, + str(self.cfgs[2].find_orientations.orientation_maps.file), test_data['existing_file'] ) diff --git a/tests/config/test_root.py b/tests/config/test_root.py index 889a23c60..db829dde1 100644 --- a/tests/config/test_root.py +++ b/tests/config/test_root.py @@ -40,7 +40,7 @@ def get_reference_data(cls): def test_analysis_dir(self): self.assertEqual( - self.cfgs[0].analysis_dir, + str(self.cfgs[0].analysis_dir), os.path.join(os.getcwd(), 'analysis') ) @@ -56,11 +56,15 @@ def test_section_inheritance(self): self.assertEqual(self.cfgs[2].analysis_name, 'analysis_2') def test_working_dir(self): - self.assertEqual(self.cfgs[0].working_dir, os.getcwd()) - self.assertEqual(self.cfgs[1].working_dir, test_data['existing_path']) + self.assertEqual(str(self.cfgs[0].working_dir), os.getcwd()) + self.assertEqual( + str(self.cfgs[1].working_dir), test_data['existing_path'] + ) self.assertRaises(IOError, getattr, self.cfgs[2], 'working_dir') self.cfgs[7].working_dir = test_data['existing_path'] - self.assertEqual(self.cfgs[7].working_dir, test_data['existing_path']) + self.assertEqual( + str(self.cfgs[7].working_dir), test_data['existing_path'] + ) self.assertRaises( IOError, setattr, self.cfgs[7], 'working_dir', test_data['nonexistent_path']