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

When directly fitting a sasmodels model from bumps the best fit theory function is not stored #601

Open
pkienzle opened this issue Jul 8, 2024 · 1 comment

Comments

@pkienzle
Copy link
Contributor

pkienzle commented Jul 8, 2024

It looks like I didn't implement problem.save() for all sasdata types in the sassmodels bumps wrapper:

def save(self, basename):
# type: (str) -> None
"""
Save the model parameters and data into a file.
Not Implemented except for sesans fits.
"""
if self.data_type == "sesans":
np.savetxt(basename+".dat", np.array([self._data.x, self.theory()]).T)

That's because the data object for sasmodels is messy:

def _interpret_data(self, data: Data, model: KernelModel) -> None:
# not type: (Data, KernelModel) -> None
# pylint: disable=attribute-defined-outside-init
self._data = data
self._model = model
# interpret data
if getattr(data, 'isSesans', False):
self.data_type = 'sesans'
elif hasattr(data, 'qx_data'):
self.data_type = 'Iqxy'
elif getattr(data, 'oriented', False):
self.data_type = 'Iq-oriented'
else:
self.data_type = 'Iq'
if self.data_type == 'sesans':
res = _make_sesans_transform(data)
index = slice(None, None)
if data.y is not None:
Iq, dIq = data.y, data.dy
else:
Iq, dIq = None, None
elif self.data_type == 'Iqxy':
#if not model.info.parameters.has_2d:
# raise ValueError("not 2D without orientation or magnetic parameters")
q = np.sqrt(data.qx_data**2 + data.qy_data**2)
qmin = getattr(data, 'qmin', 1e-16)
qmax = getattr(data, 'qmax', np.inf)
accuracy = getattr(data, 'accuracy', 'Low')
index = (data.mask == 0) & (q >= qmin) & (q <= qmax)
if data.data is not None:
index &= ~np.isnan(data.data)
Iq = data.data[index]
dIq = data.err_data[index]
else:
Iq, dIq = None, None
res = resolution2d.Pinhole2D(data=data, index=index,
nsigma=3.0, accuracy=accuracy)
elif self.data_type == 'Iq':
index = (data.x >= data.qmin) & (data.x <= data.qmax)
mask = getattr(data, 'mask', None)
if mask is not None:
index &= (mask == 0)
if data.y is not None:
index &= ~np.isnan(data.y)
Iq = data.y[index]
dIq = data.dy[index]
else:
Iq, dIq = None, None
if getattr(data, 'dx', None) is not None:
q, dq = data.x[index], data.dx[index]
if (dq > 0).any():
res = resolution.Pinhole1D(q, dq)
else:
res = resolution.Perfect1D(q)
elif (getattr(data, 'dxl', None) is not None
or getattr(data, 'dxw', None) is not None):
res = resolution.Slit1D(
data.x[index],
q_length=None if data.dxl is None else data.dxl[index],
q_width=None if data.dxw is None else data.dxw[index])
else:
res = resolution.Perfect1D(data.x[index])
elif self.data_type == 'Iq-oriented':
index = (data.x >= data.qmin) & (data.x <= data.qmax)
if data.y is not None:
index &= ~np.isnan(data.y)
Iq = data.y[index]
dIq = data.dy[index]
else:
Iq, dIq = None, None
if (getattr(data, 'dxl', None) is None
or getattr(data, 'dxw', None) is None):
raise ValueError("oriented sample with 1D data needs slit resolution")
res = resolution2d.Slit2D(
data.x[index],
qx_width=data.dxw[index],
qy_width=data.dxl[index])
else:
raise ValueError("Unknown data type") # never gets here
# Remember function inputs so we can delay loading the function and
# so we can save/restore state
self._kernel = None
self.Iq, self.dIq, self.index = Iq, dIq, index
self.resolution = res
self.results = None # type: Optional[Callable[[], OrderedDict]]

To add simple 1D sans data I think we could rewrite the save as follows (untested):

def save(self, basename):
    # type: (str) -> None
    """
    Save the model parameters and data into a file.

    Not Implemented except for sesans fits.
    """
    if self.data_type == "sesans":
        np.savetxt(basename+".dat", np.array([self._data.x, self.theory()]).T)
    elif self.data_type == "Iq":
        data, index = self._data.data, self._data.index
        x, y, dy, dx = data.x[index], self.theory(), data.dy[index], data.dx[index]
        np.savetxt(basename+".dat", np.array([x, y, dy, dx]).T)

For now you can monkey-patch this into sasmodels at the start of your script:

from sasmodels.bumps_model import Experiment
Experiment.save = save
@pkienzle
Copy link
Contributor Author

pkienzle commented Jul 8, 2024

… and extend this for the other data types return by sasdata.

Particularly messy is the adhoc masking I do within the _interpret_data method, which means the Iq returned by the theory calculation doesn't align with the measured q or (qx,qy) values.

Related: the complexity required for save already lives in the sasmodels plotting routines.

We could have a method to set set up the data structures needed for save/plot in one function so that they only need to render the data to a file in save or to a graph in plot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant