Skip to content
Snippets Groups Projects
Commit 8910a536 authored by Wuttke, Joachim's avatar Wuttke, Joachim
Browse files

mv fit monitors to separate Py module

parent 2d4d8b2e
No related branches found
No related tags found
No related merge requests found
# ************************************************************************** #
"""
# BornAgain: simulate and fit scattering at grazing incidence
#
# @file Wrap/Python/fit_monitor.py
# @brief Plotter classes for monitoring fit progress.
#
# @homepage http://apps.jcns.fz-juelich.de/BornAgain
# @license GNU General Public License v3 or higher (see COPYING)
# @copyright Forschungszentrum Juelich GmbH 2019
# @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
"""
# ************************************************************************** #
import plot_utils
class Plotter:
"""
Draws fit progress. Base class for simulation-specific classes (PlotterGISAS etc).
"""
def __init__(self,
zmin=None,
zmax=None,
xlabel=None,
ylabel=None,
units=ba.Axes.DEFAULT,
aspect=None):
self._fig = plt.figure(figsize=(10.25, 7.69))
self._fig.canvas.draw()
self._zmin = zmin
self._zmax = zmax
self._xlabel = xlabel
self._ylabel = ylabel
self._units = units
self._aspect = aspect
def reset(self):
self._fig.clf()
def plot(self):
self._fig.tight_layout()
plt.pause(0.03)
class PlotterGISAS(Plotter):
"""
Draws fit progress, for GISAS simulation.
"""
def __init__(self,
zmin=None,
zmax=None,
xlabel=None,
ylabel=None,
units=ba.Axes.DEFAULT,
aspect=None):
Plotter.__init__(self, zmin, zmax, xlabel, ylabel, units, aspect)
@staticmethod
def make_subplot(nplot):
plt.subplot(2, 2, nplot)
plt.subplots_adjust(wspace=0.2, hspace=0.2)
def plot(self, fit_objective):
Plotter.reset(self)
real_data = fit_objective.experimentalData()
sim_data = fit_objective.simulationResult()
diff = fit_objective.absoluteDifference()
self.make_subplot(1)
# same limits for both plots
arr = real_data.array()
zmax = np.amax(arr) if self._zmax is None else self._zmax
zmin = zmax*1e-6 if self._zmin is None else self._zmin
ba.plot_colormap(real_data,
title="Experimental data",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(2)
ba.plot_colormap(sim_data,
title="Simulated data",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(3)
ba.plot_colormap(diff,
title="Difference",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(4)
plt.title('Parameters')
plt.axis('off')
iteration_info = fit_objective.iterationInfo()
plt.text(
0.01, 0.85,
"Iterations " + '{:d}'.format(iteration_info.iterationCount()))
plt.text(0.01, 0.75,
"Chi2 " + '{:8.4f}'.format(iteration_info.chi2()))
index = 0
params = iteration_info.parameterMap()
for key in params:
plt.text(0.01, 0.55 - index*0.1,
'{:30.30s}: {:6.3f}'.format(key, params[key]))
index = index + 1
Plotter.plot(self)
class PlotterSpecular(Plotter):
"""
Draws fit progress, for specular simulation.
"""
def __init__(self, units=ba.Axes.DEFAULT):
Plotter.__init__(self)
self.gs = gridspec.GridSpec(1, 2, width_ratios=[2.5, 1], wspace=0)
self.units = units
def __call__(self, fit_objective):
self.plot(fit_objective)
@staticmethod
def as_si(val, ndp):
"""
Fancy print of scientific-formatted values
:param val: numeric value
:param ndp: number of decimal digits to print
:return: a string corresponding to the _val_
"""
s = '{x:0.{ndp:d}e}'.format(x=val, ndp=ndp)
m, e = s.split('e')
return r'{m:s}\times 10^{{{e:d}}}'.format(m=m, e=int(e))
@staticmethod
def trunc_str(token, length):
"""
Truncates token if it is longer than length.
Example:
trunc_str("123456789", 8) returns "123456.."
trunc_str("123456789", 9) returns "123456789"
:param token: input string
:param length: max non-truncated length
:return:
"""
return (token[:length - 2] + '..') if len(token) > length else token
def plot_table(self, fit_objective):
iteration_info = fit_objective.iterationInfo()
trunc_length = 9 # max string field width in the table
n_digits = 1 # number of decimal digits to print
n_iterations = iteration_info.iterationCount(
) # current number of iterations passed
rel_dif = fit_objective.relativeDifference().array().max(
) # maximum relative difference
fitted_parameters = iteration_info.parameterMap()
# creating table content
labels = ("Parameter", "Value")
table_data = [["Iteration", '${:d}$'.format(n_iterations)],
[
"$d_{r, max}$",
'${:s}$'.format(self.as_si(rel_dif, n_digits))
]]
for key, value in fitted_parameters.iteritems():
table_data.append([
'{:s}'.format(self.trunc_str(key, trunc_length)),
'${:s}$'.format(self.as_si(value, n_digits))
])
# creating table
axs = plt.subplot(self.gs[1])
axs.axis('tight')
axs.axis('off')
table = plt.table(cellText=table_data,
colLabels=labels,
cellLoc='center',
loc='bottom left',
bbox=[0.0, 0.0, 1.0, 1.0])
def plot_graph(self, fit_objective):
# retrieving data from fit suite
real_data = fit_objective.experimentalData()
sim_data = fit_objective.simulationResult()
unc_data = fit_objective.uncertaintyData()
# data values
sim_values = sim_data.array(self.units)
real_values = real_data.array(self.units)
unc_values = None if unc_data is None else unc_data.array(self.units)
# default font properties dictionary to use
font = {'family': 'serif', 'weight': 'normal', 'size': label_fontsize}
plt.subplot(self.gs[0])
plt.semilogy(sim_data.axis(), sim_values, 'b', real_data.axis(),
real_values, 'k--')
if unc_values is not None:
plt.semilogy(real_data.axis(),
real_values - unc_values,
'xkcd:grey',
alpha=0.6)
plt.semilogy(real_data.axis(),
real_values + unc_values,
'xkcd:grey',
alpha=0.6)
plt.ylim((0.5*np.min(real_values), 5*np.max(real_values)))
xlabel = get_axes_labels(real_data, self.units)[0]
legend = ['BornAgain', 'Data']
if unc_values is not None:
legend = ['BornAgain', 'Data', r'Data $\pm \sigma$']
plt.legend(legend, loc='upper right', prop=font)
plt.xlabel(xlabel, fontdict=font)
plt.ylabel("Intensity", fontdict=font)
plt.title("Specular data fitting", fontdict=font)
def plot(self, fit_objective):
Plotter.reset(self)
self.plot_graph(fit_objective)
self.plot_table(fit_objective)
Plotter.plot(self)
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
""" """
# BornAgain: simulate and fit scattering at grazing incidence # BornAgain: simulate and fit scattering at grazing incidence
# #
# @file Wrap/Python.plot_utils # @file Wrap/Python/plot_utils.py
# @brief Python extensions of the SWIG-generated Python module bornagain. # @brief Python extensions of the SWIG-generated Python module bornagain.
# #
# @homepage http://apps.jcns.fz-juelich.de/BornAgain # @homepage http://apps.jcns.fz-juelich.de/BornAgain
# @license GNU General Public License v3 or higher (see COPYING) # @license GNU General Public License v3 or higher (see COPYING)
# @copyright Forschungszentrum Juelich GmbH 2016 # @copyright Forschungszentrum Juelich GmbH 2016
# @authors Scientific Computing Group at MLZ Garching # @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
# @authors J. Fisher, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
""" """
# ************************************************************************** # # ************************************************************************** #
...@@ -48,19 +47,19 @@ def translate_axis_label(label): ...@@ -48,19 +47,19 @@ def translate_axis_label(label):
""" """
label_dict = { label_dict = {
'X [nbins]': r'$X \; $(bins)', 'X [nbins]': r'$X \; $(bins)',
'X [mm]': r'$X \; $(mm)',
'Y [nbins]': r'$Y \; $(bins)',
'Y [mm]': r'$Y \; $(mm)',
'phi_f [rad]': r'$\varphi_f \; $(rad)', 'phi_f [rad]': r'$\varphi_f \; $(rad)',
'phi_f [deg]': r'$\varphi_f \; $(deg)', 'phi_f [deg]': r'$\varphi_f \; $(deg)',
'alpha_i [rad]': r'$\alpha_i \; $(rad)', 'alpha_i [rad]': r'$\alpha_i \; $(rad)',
'alpha_i [deg]': r'$\alpha_i \; $(deg)', 'alpha_i [deg]': r'$\alpha_i \; $(deg)',
'X [mm]': r'$X \; $(mm)',
'Qx [1/nm]': r'$Q_x \; $(nm$^{-1}$)',
'Qy [1/nm]': r'$Q_y \; $(nm$^{-1}$)',
'Q [1/nm]': r'$Q \; $(nm$^{-1}$)',
'Y [nbins]': r'$Y \; $(bins)',
'alpha_f [rad]': r'$\alpha_f \; $(rad)', 'alpha_f [rad]': r'$\alpha_f \; $(rad)',
'alpha_f [deg]': r'$\alpha_f \; $(deg)', 'alpha_f [deg]': r'$\alpha_f \; $(deg)',
'Y [mm]': r'$Y \; $(mm)', 'Qx [1/nm]': r'$Q_x \; $(nm$^{-1}$)',
'Qy [1/nm]': r'$Q_y \; $(nm$^{-1}$)',
'Qz [1/nm]': r'$Q_z \; $(nm$^{-1}$)', 'Qz [1/nm]': r'$Q_z \; $(nm$^{-1}$)',
'Q [1/nm]': r'$Q \; $(nm$^{-1}$)',
'Position [nm]': r'$Position \; $(nm)' 'Position [nm]': r'$Position \; $(nm)'
} }
if label in label_dict.keys(): if label in label_dict.keys():
...@@ -265,234 +264,3 @@ def plot_simulation_result(result, ...@@ -265,234 +264,3 @@ def plot_simulation_result(result,
plt.tight_layout() plt.tight_layout()
if not postpone_show: if not postpone_show:
plt.show() plt.show()
class Plotter:
def __init__(self,
zmin=None,
zmax=None,
xlabel=None,
ylabel=None,
units=ba.Axes.DEFAULT,
aspect=None):
self._fig = plt.figure(figsize=(10.25, 7.69))
self._fig.canvas.draw()
self._zmin = zmin
self._zmax = zmax
self._xlabel = xlabel
self._ylabel = ylabel
self._units = units
self._aspect = aspect
def reset(self):
self._fig.clf()
def plot(self):
self._fig.tight_layout()
plt.pause(0.03)
class PlotterGISAS(Plotter):
def __init__(self,
zmin=None,
zmax=None,
xlabel=None,
ylabel=None,
units=ba.Axes.DEFAULT,
aspect=None):
Plotter.__init__(self, zmin, zmax, xlabel, ylabel, units, aspect)
@staticmethod
def make_subplot(nplot):
plt.subplot(2, 2, nplot)
plt.subplots_adjust(wspace=0.2, hspace=0.2)
def plot(self, fit_objective):
Plotter.reset(self)
real_data = fit_objective.experimentalData()
sim_data = fit_objective.simulationResult()
diff = fit_objective.absoluteDifference()
self.make_subplot(1)
# same limits for both plots
arr = real_data.array()
zmax = np.amax(arr) if self._zmax is None else self._zmax
zmin = zmax*1e-6 if self._zmin is None else self._zmin
ba.plot_colormap(real_data,
title="Experimental data",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(2)
ba.plot_colormap(sim_data,
title="Simulated data",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(3)
ba.plot_colormap(diff,
title="Difference",
zmin=zmin,
zmax=zmax,
units=self._units,
xlabel=self._xlabel,
ylabel=self._ylabel,
zlabel='',
aspect=self._aspect)
self.make_subplot(4)
plt.title('Parameters')
plt.axis('off')
iteration_info = fit_objective.iterationInfo()
plt.text(
0.01, 0.85,
"Iterations " + '{:d}'.format(iteration_info.iterationCount()))
plt.text(0.01, 0.75,
"Chi2 " + '{:8.4f}'.format(iteration_info.chi2()))
index = 0
params = iteration_info.parameterMap()
for key in params:
plt.text(0.01, 0.55 - index*0.1,
'{:30.30s}: {:6.3f}'.format(key, params[key]))
index = index + 1
Plotter.plot(self)
class PlotterSpecular(Plotter):
"""
Draws fit progress. Intended specifically for observing
specular data fit.
"""
def __init__(self, units=ba.Axes.DEFAULT):
Plotter.__init__(self)
self.gs = gridspec.GridSpec(1, 2, width_ratios=[2.5, 1], wspace=0)
self.units = units
def __call__(self, fit_objective):
self.plot(fit_objective)
@staticmethod
def as_si(val, ndp):
"""
Fancy print of scientific-formatted values
:param val: numeric value
:param ndp: number of decimal digits to print
:return: a string corresponding to the _val_
"""
s = '{x:0.{ndp:d}e}'.format(x=val, ndp=ndp)
m, e = s.split('e')
return r'{m:s}\times 10^{{{e:d}}}'.format(m=m, e=int(e))
@staticmethod
def trunc_str(token, length):
"""
Truncates token if it is longer than length.
Example:
trunc_str("123456789", 8) returns "123456.."
trunc_str("123456789", 9) returns "123456789"
:param token: input string
:param length: max non-truncated length
:return:
"""
return (token[:length - 2] + '..') if len(token) > length else token
def plot_table(self, fit_objective):
iteration_info = fit_objective.iterationInfo()
trunc_length = 9 # max string field width in the table
n_digits = 1 # number of decimal digits to print
n_iterations = iteration_info.iterationCount(
) # current number of iterations passed
rel_dif = fit_objective.relativeDifference().array().max(
) # maximum relative difference
fitted_parameters = iteration_info.parameterMap()
# creating table content
labels = ("Parameter", "Value")
table_data = [["Iteration", '${:d}$'.format(n_iterations)],
[
"$d_{r, max}$",
'${:s}$'.format(self.as_si(rel_dif, n_digits))
]]
for key, value in fitted_parameters.iteritems():
table_data.append([
'{:s}'.format(self.trunc_str(key, trunc_length)),
'${:s}$'.format(self.as_si(value, n_digits))
])
# creating table
axs = plt.subplot(self.gs[1])
axs.axis('tight')
axs.axis('off')
table = plt.table(cellText=table_data,
colLabels=labels,
cellLoc='center',
loc='bottom left',
bbox=[0.0, 0.0, 1.0, 1.0])
def plot_graph(self, fit_objective):
# retrieving data from fit suite
real_data = fit_objective.experimentalData()
sim_data = fit_objective.simulationResult()
unc_data = fit_objective.uncertaintyData()
# data values
sim_values = sim_data.array(self.units)
real_values = real_data.array(self.units)
unc_values = None if unc_data is None else unc_data.array(self.units)
# default font properties dictionary to use
font = {'family': 'serif', 'weight': 'normal', 'size': label_fontsize}
plt.subplot(self.gs[0])
plt.semilogy(sim_data.axis(), sim_values, 'b', real_data.axis(),
real_values, 'k--')
if unc_values is not None:
plt.semilogy(real_data.axis(),
real_values - unc_values,
'xkcd:grey',
alpha=0.6)
plt.semilogy(real_data.axis(),
real_values + unc_values,
'xkcd:grey',
alpha=0.6)
plt.ylim((0.5*np.min(real_values), 5*np.max(real_values)))
xlabel = get_axes_labels(real_data, self.units)[0]
legend = ['BornAgain', 'Data']
if unc_values is not None:
legend = ['BornAgain', 'Data', r'Data $\pm \sigma$']
plt.legend(legend, loc='upper right', prop=font)
plt.xlabel(xlabel, fontdict=font)
plt.ylabel("Intensity", fontdict=font)
plt.title("Specular data fitting", fontdict=font)
def plot(self, fit_objective):
Plotter.reset(self)
self.plot_graph(fit_objective)
self.plot_table(fit_objective)
Plotter.plot(self)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment