diff --git a/ImageD11/parameters.py b/ImageD11/parameters.py index b3234f62..39a1fcc3 100644 --- a/ImageD11/parameters.py +++ b/ImageD11/parameters.py @@ -1 +1,310 @@ -from xfab.parameters import * +# temporary comment out for new pars development: +# from xfab.parameters import * + +from __future__ import print_function + +# ImageD11_v0.4 Software for beamline ID11 +# Copyright (C) 2005 Jon Wright +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# Moved from ImageD11 to xfab 07/11/2019 + +""" +Class to handle groups of parameters to be saved in and out +of files and edited in guis with the fixed/varied info etc +""" + + +from xfab import xfab_logging + +logger = xfab_logging.get_module_level_logger(__name__) + + +class AnalysisSchema: + """Class to handle more complicated data analysis parameters.""" + + def __init__(self, filename=None): + # the dictionary of everything we found from file + self.pars_dict = dict() + + # a parameters object for the geometry + self.geometry_pars_obj = None + + # a dict of parameters object for each phase + self.phase_pars_obj_dict = dict() + + if filename is not None: + self.load_json(filename) + self.get_pars_objects() + + def get_pars_objects(self): + """Parses self.pars_dict, reads the .par files, makes parameter objects for them""" + # get geometric parameters + geometry_dict = self.pars_dict["geometry"] + geometry_file = geometry_dict["file"] + self.geometry_pars_obj = parameters() + self.geometry_pars_obj.loadparameters(geometry_file) + + # get phase parameters from one of the phases + phases_dict = self.pars_dict["phases"] + for phase_name, phase_entry in phases_dict.items(): + phase_file = phase_entry["file"] + # read the phase pars from disk + phase_pars_obj = parameters() + phase_pars_obj.loadparameters(phase_file) + # put this pars object in self.phase_pars_obj_dict + self.phase_pars_obj_dict[phase_name] = phase_pars_obj + + def get_any_phase_pars_obj(self): + """Returns any parameters object from self.phase_pars_obj_dict""" + return next(iter(self.phase_pars_obj_dict.values())) + + @property + def xfab_pars_dict(self): + """Build a xfab pars compatible dictionary""" + # get geometry pars as a dict + geometry_pars_dict = self.geometry_pars_obj.get_parameters() + # get any phase pars as a dict + phase_pars_dict = self.get_any_phase_pars_obj().get_parameters() + # combine dicts together + pars_dict = phase_pars_dict.copy() + pars_dict.update(geometry_pars_dict) + return pars_dict + + def load_json(self, filename): + """ + Load json from file + """ + import json + with open(filename, 'r') as json_string: + self.pars_dict = json.load(json_string) + + +class par: + """ + Represents a thing which can vary + """ + + def __init__(self, name, value, helpstring=None, + vary=False, can_vary=False, stepsize=None): + """ + name : unique key used as keyword arg to some functions + value : value of the parameter + helpstring : optional string to help user + vary : value should be optimised + can_vary : value is not fixed + stepsize : guessestimated ball park step size (eg not 1e99!) + """ + self.name = name + self.value = value + if helpstring is None: + self.helpstring = "parameter : " + name + else: + self.helpstring = helpstring + self.vary = vary + self.can_vary = can_vary + self.stepsize = stepsize + + def fromstringlist(self, sl): + """ to send to Java """ + [self.name, + self.value, + self.helpstring, + self.vary, + self.can_vary, + self.stepsize] = sl + + def tostringlist(self): + """ to catch from Java """ + return [self.name, + self.value, + self.helpstring, + self.vary, + self.can_vary, + self.stepsize] + + +class parameters: + """ + Class to hold a set of named parameters + """ + + def __init__(self, **kwds): + """ + name=value style arg list + """ + self.parameters = kwds + self.varylist = [] + self.can_vary = {} + self.variable_list = [] + self.stepsizes = {} + self.par_objs = {} + for k, v in list(self.parameters.items()): + self.addpar(par(k, v)) + + def addpar(self, par): + """ + add a parameter object + """ + self.parameters[par.name] = par.value + self.can_vary[par.name] = par.can_vary + if par.vary and par.name not in self.varylist: + self.varylist.append(par.name) + if par.can_vary and par.name not in self.variable_list: + self.variable_list.append(par.name) + self.stepsizes[par.name] = par.stepsize + self.par_objs[par.name] = par + + def get_variable_list(self): + return self.variable_list + + def get_variable_values(self): + """ values of the parameters """ + return [self.parameters[name] for name in self.varylist] + + def get_variable_stepsizes(self): + """ stepsizes for optimisers """ + return [self.stepsizes[name] for name in self.varylist] + + def set_varylist(self, vl): + ks = list(self.parameters.keys()) + for v in vl: + assert v in ks + assert v in self.variable_list + self.varylist = vl + + def set_variable_values(self, values): + """ set values of the parameters""" + assert len(values) == len(self.varylist) + for name, value in zip(self.varylist, values): + self.parameters[name] = value + + def set_parameters(self, d): + """ + Updates the values of parameters + """ + self.parameters.update(d) + self.dumbtypecheck() + + def get_parameters(self): + """ + Returns a dictionary of parameters + """ + return self.parameters + + def get(self, name): + return self.parameters[name] + + def set(self, name, value): + self.parameters[name] = value + + def update_yourself(self, other): + """ + Sychronise this parameter objects list of values with another object + """ + for k, v in list(self.parameters.items()): + if hasattr(other, k): + var = getattr(other, k) + logger.debug("setting: pars[%s] from %s to %s" % (k, v, var)) + self.parameters[k] = var + else: + logger.debug("error: %s has no attribute %s, ignoring" % (other, k)) + + def update_other(self, other): + """ + Synchronise an object with the values in this object + """ + for k, v in list(self.parameters.items()): + if hasattr(other, k): + var = getattr(other, k) + logger.debug("setting: %s.%s from %s to %s" % (other, k, var, v)) + setattr(other, k, v) + else: + logger.debug("error: %s has no attribute %s, ignoring" % + (other, k)) + + def saveparameters(self, filename): + """ + Write parameters to a file + """ + f = open(filename, "w") + keys = list(self.parameters.keys()) + keys.sort() + for key in keys: + f.write("%s %s\n" % (key, str(self.parameters[key]))) + f.close() + + def loadparameters(self, filename): + """ + Load parameters from a file + """ + # is it a json file? + if filename.endswith('json'): + # create a JsonPars object and get the pars dict from it + pars_dict = AnalysisSchema(filename=filename).xfab_pars_dict + self.parameters.update(pars_dict) + else: + lines = open(filename, "r").readlines() + for line in lines: + try: + [name, value] = line.split(" ") + name = name.replace("-", "_") + self.parameters[name] = value + except ValueError: + logger.error("Failed to read:%s" % (line)) + self.dumbtypecheck() + + def dumbtypecheck(self): + """ + Eventually parameter types (and units and fixed/varied to be + specifieable + For now it just tries to coerce to float, then does nothing + """ + for name, value in list(self.parameters.items()): + if type(value) == type("string"): + try: + vf = float(value) + except ValueError: + # it really is a string + self.parameters[name] = value.lstrip().rstrip() + continue + # here if float worked + try: + vi = int(value) + except ValueError: + # it really is a float + self.parameters[name] = vf + continue + + # here if float and int worked + # should not be needed, depends on int valueerror + if abs(vi - vf) < 1e-9: + # use int + self.parameters[name] = vi + continue + else: + self.parameters[name] = vf + continue + else: + # int/float preserve type + self.parameters[name] = value + + +def read_par_file(filename): + p = parameters() + p.loadparameters(filename) + return p + diff --git a/ImageD11/unitcell.py b/ImageD11/unitcell.py index fe54b9bb..fc36cb70 100644 --- a/ImageD11/unitcell.py +++ b/ImageD11/unitcell.py @@ -33,6 +33,7 @@ from ImageD11 import cImageD11 from xfab import tools from scipy.spatial.transform import Rotation as ScipyRotation +from ImageD11.parameters import AnalysisSchema def radians(x): @@ -158,6 +159,24 @@ def cellfromstring(s): return unitcell(latt, symm) +class Phases(AnalysisSchema): + """ + Phases class - extends AnalysisSchema from xfab.parameters + Reads analysis parameters from file + Contains self.unitcells which is a dict of unitcell objects + """ + def __init__(self, filename): + super(Phases, self).__init__(filename) + self.unitcells = {} + if filename is not None: + self.get_unitcells() + + def get_unitcells(self): + # dict of parameter objects for each phase + for phase_name, phase_pars_obj in self.phase_pars_obj_dict.items(): + self.unitcells[phase_name] = unitcell_from_parameters(phase_pars_obj) + + class unitcell: # Unit cell stuff # Generate a list of peaks from a unit cell diff --git a/sandbox/jadb/multiphase/austenite.par b/sandbox/jadb/multiphase/austenite.par new file mode 100644 index 00000000..b4776be7 --- /dev/null +++ b/sandbox/jadb/multiphase/austenite.par @@ -0,0 +1,7 @@ +cell__a 3.66 +cell__b 3.66 +cell__c 3.66 +cell_alpha 90.0 +cell_beta 90.0 +cell_gamma 90.0 +cell_lattice_[P,A,B,C,I,F,R] 225 \ No newline at end of file diff --git a/sandbox/jadb/multiphase/ferrite.par b/sandbox/jadb/multiphase/ferrite.par new file mode 100644 index 00000000..906fba56 --- /dev/null +++ b/sandbox/jadb/multiphase/ferrite.par @@ -0,0 +1,7 @@ +cell__a 2.8729 +cell__b 2.8729 +cell__c 2.8729 +cell_alpha 90.0 +cell_beta 90.0 +cell_gamma 90.0 +cell_lattice_[P,A,B,C,I,F,R] 229 \ No newline at end of file diff --git a/sandbox/jadb/multiphase/geometry.par b/sandbox/jadb/multiphase/geometry.par new file mode 100644 index 00000000..39d7db58 --- /dev/null +++ b/sandbox/jadb/multiphase/geometry.par @@ -0,0 +1,24 @@ +chi 0.0 +distance 135969.66817479226 +fit_tol 0.05 +fit_tolerance 0.05 +min_bin_prob 1e-05 +no_bins 10000 +o11 1 +o12 0 +o21 0 +o22 -1 +omegasign 1.0 +t_x 0.0 +t_y 0.0 +t_z 0.0 +tilt_x -0.008218375579544133 +tilt_y 0.0047234636502828855 +tilt_z 0.0008764776070003078 +wavelength 0.28457041 +wedge -0.0059951962535988255 +weight_hist_intensities False +y_center 1081.9695550770361 +y_size 47.0 +z_center 1015.0818279709029 +z_size 47.0 diff --git a/sandbox/jadb/multiphase/par_json_test.py b/sandbox/jadb/multiphase/par_json_test.py new file mode 100644 index 00000000..59c66662 --- /dev/null +++ b/sandbox/jadb/multiphase/par_json_test.py @@ -0,0 +1,60 @@ +# exec(open('/data/id11/nanoscope/install_ImageD11_from_git.py').read()) + +# Add ImageD11 local +# PYTHONPATH = setup_ImageD11_from_git( os.path.join( os.environ['HOME'],'Code'), 'ImageD11' ) + +from ImageD11.parameters import parameters, AnalysisSchema +from ImageD11.unitcell import Phases +from ImageD11.columnfile import columnfile + + +def main(): + # read a json file into AnalysisSchema + json_pars = AnalysisSchema('pars.json') + + # print the json dict + print(json_pars.pars_dict) + + # print the geometry pars object + print(json_pars.geometry_pars_obj.get_parameters()) + + # print the phase pars object dict + print(json_pars.phase_pars_obj_dict) + + # print a specific phase pars object + print(json_pars.phase_pars_obj_dict["ferrite"].get_parameters()) + + # print a combined pars dict + print(json_pars.xfab_pars_dict) + + # read a json file directly with parameters.loadparameters + pars = parameters() + pars.loadparameters(filename='pars.json') + + # print the pars + print(pars.get_parameters()) + + # load a columnfile + cf = columnfile('peaks.flt') + + # print the header parameters + print(cf.parameters.get_parameters()) + + # print the lattice (should be 'I' for the header parameters) + print(cf.parameters.get('cell_lattice_[P,A,B,C,I,F,R]')) + + # load the cf parameters from disk + cf.parameters.loadparameters('pars.json') + + # print the lattice (should be 229 for the JSON parameters) + print(cf.parameters.get('cell_lattice_[P,A,B,C,I,F,R]')) + + # import the phases + phases = Phases('pars.json') + + # print the unitcells dict + print(phases.unitcells) + + +if __name__ == '__main__': + main() diff --git a/sandbox/jadb/multiphase/pars.json b/sandbox/jadb/multiphase/pars.json new file mode 100644 index 00000000..86f087f6 --- /dev/null +++ b/sandbox/jadb/multiphase/pars.json @@ -0,0 +1,16 @@ +{ + "geometry": { + "file":"geometry.par", + "hash": "MD5HASHGEO" + }, + "phases": { + "ferrite": { + "file": "ferrite.par", + "hash": "MD5HASH1" + }, + "austenite": { + "file": "austenite.par", + "hash": "MD5HASH2" + } + } +} \ No newline at end of file diff --git a/sandbox/jadb/multiphase/peaks.flt b/sandbox/jadb/multiphase/peaks.flt new file mode 100644 index 00000000..3c9c4a23 --- /dev/null +++ b/sandbox/jadb/multiphase/peaks.flt @@ -0,0 +1,33 @@ +# cell__a = 2.8729 +# cell__b = 2.8729 +# cell__c = 2.8729 +# cell_alpha = 90.0 +# cell_beta = 90.0 +# cell_gamma = 90.0 +# cell_lattice_[P,A,B,C,I,F,R] = I +# chi = 0.0 +# distance = 135969.66817479226 +# fit_tol = 0.05 +# fit_tolerance = 0.05 +# min_bin_prob = 1e-05 +# no_bins = 10000 +# o11 = 1 +# o12 = 0 +# o21 = 0 +# o22 = -1 +# omegasign = 1.0 +# t_x = 0.0 +# t_y = 0.0 +# t_z = 0.0 +# tilt_x = -0.008218375579544133 +# tilt_y = 0.0047234636502828855 +# tilt_z = 0.0008764776070003078 +# wavelength = 0.28457041 +# wedge = -0.0059951962535988255 +# weight_hist_intensities = False +# y_center = 1081.9695550770361 +# y_size = 47.0 +# z_center = 1015.0818279709029 +# z_size = 47.0 +# sc fc omega Number_of_pixels s_raw f_raw sum_intensity xl yl zl tth eta gx gy gz Lorentz Lorentz_per_grain drlv2 ds eta_per_grain h hr index k kr l labels lr omegacalc_per_grain tth_per_grain + 323.5352 893.7333 1.2835 1378 331.1430 889.9982 775122.4236 135808.3893 8579.6888 -32573.9027 13.9301 -165.2439 -0.0984 0.2177 -0.8181 0.061317 0.060563 0.0000 0.852258 -165.438721 -1 -0.9997 6.000000 1 1.0001 -2 45 -1.999502 1.211893 13.938727 \ No newline at end of file