Skip to content

Commit 6cc28a0

Browse files
author
Paul Kienzle
committed
Fix pickle errors with product models so sasmodels can be used with bumps
1 parent 8042f91 commit 6cc28a0

File tree

2 files changed

+38
-28
lines changed

2 files changed

+38
-28
lines changed

sasmodels/product.py

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,6 @@ def make_product_info(p_info, s_info):
220220
#s_list[0] = copy(s_list[0])
221221
#s_list[0].polydisperse = False
222222

223-
s_translate = {old.id: new.id for old, new in s_pairs}
224-
def random():
225-
"""Random set of model parameters for product model"""
226-
combined_pars = p_info.random()
227-
combined_pars.update((s_translate[k], v)
228-
for k, v in s_info.random().items()
229-
if k in s_translate)
230-
return combined_pars
231-
232223
model_info = ModelInfo()
233224
model_info.id = '@'.join((p_id, s_id))
234225
model_info.name = '@'.join((p_name, s_name))
@@ -238,7 +229,7 @@ def random():
238229
model_info.docs = model_info.title
239230
model_info.category = "custom"
240231
model_info.parameters = parameters
241-
model_info.random = random
232+
model_info.random = _ProductRandom(p_info, s_info, s_pairs)
242233
#model_info.single = p_info.single and s_info.single
243234
model_info.structure_factor = False
244235
#model_info.tests = []
@@ -268,6 +259,19 @@ def profile(**kwargs):
268259
#print(parlist(model_info, values, is2d=True))
269260
return model_info
270261

262+
class _ProductRandom:
263+
"""Random set of model parameters for product model"""
264+
def __init__(self, p_info, s_info, s_pairs):
265+
self.p_info = p_info
266+
self.s_info = s_info
267+
self.s_translate = {old.id: new.id for old, new in s_pairs}
268+
def __call__(self):
269+
combined_pars = self.p_info.random()
270+
combined_pars.update((self.s_translate[k], v)
271+
for k, v in self.s_info.random().items()
272+
if k in self.s_translate)
273+
return combined_pars
274+
271275
def _tag_parameter(par):
272276
"""
273277
Tag the parameter name with _S to indicate that the parameter comes from
@@ -284,26 +288,30 @@ def _tag_parameter(par):
284288
par.name = par.id + vector_length
285289
return par
286290

287-
def _intermediates(Q, F, Fsq, S, scale, volume, volume_ratio, radius_effective,
288-
beta_mode, P_intermediate):
289-
# type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray, float, float, float, float, bool, Optiona[Callable[[], Parts]]) -> Parts
291+
class _Intermediates:
290292
"""
291293
Returns intermediate results for beta approximation-enabled product.
292294
The result may be an array or a float.
293295
"""
294-
parts = OrderedDict() # type: Parts
295-
parts["P(Q)"] = (Q, scale*Fsq)
296-
if P_intermediate is not None:
297-
parts["P(Q) parts"] = P_intermediate()
298-
parts["volume"] = volume
299-
parts["volume_ratio"] = volume_ratio
300-
parts["radius_effective"] = radius_effective
301-
parts["S(Q)"] = (Q, S)
302-
if beta_mode:
303-
parts["beta(Q)"] = (Q, F**2 / Fsq)
304-
parts["S_eff(Q)"] = (Q, 1 + (F**2 / Fsq)*(S-1))
305-
#parts["I(Q)", scale*(Fsq + (F**2)*(S-1)) + bg
306-
return parts
296+
def __init__(self, *args):
297+
# type: (np.ndarray, np.ndarray, np.ndarray, np.ndarray, float, float, float, float, bool, Optiona[Callable[[], Parts]]) -> Parts
298+
self.args = args
299+
def __call__(self):
300+
Q, F, Fsq, S, scale, volume, volume_ratio, radius_effective, \
301+
beta_mode, P_intermediate = self.args
302+
parts = OrderedDict() # type: Parts
303+
parts["P(Q)"] = (Q, scale*Fsq)
304+
if P_intermediate is not None:
305+
parts["P(Q) parts"] = P_intermediate()
306+
parts["volume"] = volume
307+
parts["volume_ratio"] = volume_ratio
308+
parts["radius_effective"] = radius_effective
309+
parts["S(Q)"] = (Q, S)
310+
if beta_mode:
311+
parts["beta(Q)"] = (Q, F**2 / Fsq)
312+
parts["S_eff(Q)"] = (Q, 1 + (F**2 / Fsq)*(S-1))
313+
#parts["I(Q)", scale*(Fsq + (F**2)*(S-1)) + bg
314+
return parts
307315

308316
class ProductModel(KernelModel):
309317
"""
@@ -535,7 +543,9 @@ def Iq(self, call_details, values, cutoff, magnetic):
535543
# kernel calling interface. Could do this as an "optional"
536544
# return value in the caller, though in that case we could return
537545
# the results directly rather than through a lazy evaluator.
538-
self.results = lambda: _intermediates(
546+
# TODO: why is pickle seeing self.results?
547+
# Note: turned into a callable class because pickle didn't like it.
548+
self.results = _Intermediates(
539549
self.q, F, Fsq, S, combined_scale, shell_volume, volume_ratio,
540550
radius_effective, beta_mode, p_intermediate)
541551

sasmodels/sasview_model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ def _calculate_Iq(self, qx, qy=None):
740740
magnetic=is_magnetic)
741741
lazy_results = getattr(calculator, 'results',
742742
lambda: collections.OrderedDict())
743-
#print("result", result)
743+
#print("result", result, lazy_results())
744744

745745
calculator.release()
746746
#self._model.release()

0 commit comments

Comments
 (0)