import os
from pathlib import Path
import logging
import numpy as np
from .config import Config
logger = logging.getLogger('hexrd.config')
# TODO: set these as defaults
seed_search_methods = {
'label': dict(filter_radius=1, threshold=1),
'blob_log': dict(min_sigma=0.5, max_sigma=5,
num_sigma=10, threshold=0.01,
overlap=0.1),
'blob_dog': dict(min_sigma=0.5, max_sigma=5,
sigma_ratio=1.6,
threshold=0.01, overlap=0.1)
}
[docs]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"""
root = self.parent
if self.parent.new_file_placement:
fname = f"{self._find_ori}-{self._active_material_str()}.log"
pth = root.analysis_dir / fname
else:
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()
if self.parent.new_file_placement:
newname = f"accepted-orientations-{actmat}.dat"
aof_path = self.parent.analysis_dir / newname
else:
oldname = (
'accepted_orientations_%s.dat' % self.parent.analysis_id
)
aof_path = self.parent.working_dir / oldname
return aof_path
@property
def grains_file(self):
"""Path to `grains.out` file"""
return self.parent.analysis_dir / "grains.out"
# Subsections
@property
def orientation_maps(self):
return OrientationMapsConfig(self._cfg)
@property
def seed_search(self):
return SeedSearchConfig(self._cfg)
@property
def clustering(self):
return ClusteringConfig(self._cfg)
@property
def eta(self):
return EtaConfig(self._cfg)
@property
def omega(self):
return OmegaConfig(self._cfg)
# Simple Values
@property
def threshold(self):
return self._cfg.get('find_orientations:threshold', 1)
@property
def use_quaternion_grid(self):
key = 'find_orientations:use_quaternion_grid'
temp = self._cfg.get(key, None)
if temp is None:
return temp
if not os.path.isabs(temp):
temp = os.path.join(self._cfg.working_dir, temp)
if os.path.isfile(temp):
return temp
raise IOError(
'"%s": "%s" does not exist' % (key, temp)
)
@property
def extract_measured_g_vectors(self):
return self._cfg.get(
'find_orientations:extract_measured_g_vectors',
False
)
[docs]class ClusteringConfig(Config):
@property
def algorithm(self):
key = 'find_orientations:clustering:algorithm'
choices = ['dbscan', 'ort-dbscan', 'sph-dbscan', 'fclusterdata']
temp = self._cfg.get(key, 'dbscan').lower()
if temp in choices:
return temp
raise RuntimeError(
'"%s": "%s" not recognized, must be one of %s'
% (key, temp, choices)
)
@property
def completeness(self):
key = 'find_orientations:clustering:completeness'
temp = self._cfg.get(key, None)
if temp is not None:
return temp
raise RuntimeError(
'"%s" must be specified' % key
)
@property
def radius(self):
key = 'find_orientations:clustering:radius'
temp = self._cfg.get(key, None)
if temp is not None:
return temp
raise RuntimeError(
'"%s" must be specified' % key
)
[docs]class OmegaConfig(Config):
tolerance_dflt = 0.5
@property
def period(self):
# FIXME: this is deprecated and now set from the imageseries
key = 'find_orientations:omega:period'
temp = self._cfg.get(key, [-180., 180])
range = np.abs(temp[1]-temp[0])
logger.warning('omega period specification is deprecated')
if range != 360:
raise RuntimeError(
'"%s": range must be 360 degrees, range of %s is %g'
% (key, temp, range)
)
return temp
@property
def tolerance(self):
return self._cfg.get(
'find_orientations:omega:tolerance',
self.tolerance_dflt
)
[docs]class EtaConfig(Config):
tolerance_dflt = 0.5
@property
def tolerance(self):
return self._cfg.get(
'find_orientations:eta:tolerance',
self.tolerance_dflt
)
@property
def mask(self):
return self._cfg.get('find_orientations:eta:mask', 5)
@property
def range(self):
mask = self.mask
if mask is None:
return mask
return np.array([[-90. + mask, 90. - mask], [90. + mask, 270. - mask]])
[docs]class SeedSearchConfig(Config):
@property
def hkl_seeds(self):
key = 'find_orientations:seed_search:hkl_seeds'
try:
temp = self._cfg.get(key)
if isinstance(temp, int):
temp = [temp, ]
return temp
except:
if self._cfg.find_orientations.use_quaternion_grid is None:
raise RuntimeError(
'"%s" must be defined for seeded search' % key
)
@property
def fiber_step(self):
return self._cfg.get(
'find_orientations:seed_search:fiber_step',
self._cfg.find_orientations.omega.tolerance
)
@property
def method(self):
key = 'find_orientations:seed_search:method'
try:
temp = self._cfg.get(key)
assert len(temp) == 1., \
"method must have exactly one key"
if isinstance(temp, dict):
method_spec = next(iter(list(temp.keys())))
if method_spec.lower() not in seed_search_methods:
raise RuntimeError(
'invalid seed search method "%s"'
% method_spec
)
else:
return temp
except:
raise RuntimeError(
'"%s" must be defined for seeded search' % key
)
@property
def fiber_ndiv(self):
return int(360.0 / self.fiber_step)
[docs]class OrientationMapsConfig(Config):
@property
def active_hkls(self):
temp = self._cfg.get(
'find_orientations:orientation_maps:active_hkls', default='all'
)
if isinstance(temp, int):
temp = [temp]
if temp == 'all':
temp = None
return temp
@property
def bin_frames(self):
return self._cfg.get(
'find_orientations:orientation_maps:bin_frames', default=1
)
@property
def eta_step(self):
return self._cfg.get(
'find_orientations:orientation_maps:eta_step', default=0.25
)
@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.
#
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:
return mapf
else:
ptemp = Path(temp)
if ptemp.is_absolute():
mapf = ptemp
else:
# Path is relative to working directory.
mapf = root.working_dir / ptemp
return mapf
@property
def scored_orientations_file(self):
root = self.parent
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', root.analysis_id])
sof = root.working_dir / fname
return sof
@property
def threshold(self):
return self._cfg.get('find_orientations:orientation_maps:threshold')
@property
def filter_maps(self):
return self._cfg.get('find_orientations:orientation_maps:filter_maps',
default=False)