Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for extended data analysis parameters #321

Merged
merged 4 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
311 changes: 310 additions & 1 deletion ImageD11/parameters.py
Original file line number Diff line number Diff line change
@@ -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

19 changes: 19 additions & 0 deletions ImageD11/unitcell.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions sandbox/jadb/multiphase/austenite.par

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions sandbox/jadb/multiphase/ferrite.par

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions sandbox/jadb/multiphase/geometry.par

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading