source: sasmodels/sasmodels/mixture.py @ 7ae2b7f

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 7ae2b7f was 7ae2b7f, checked in by Paul Kienzle <pkienzle@…>, 6 years ago

still more linting; ignore numpy types

  • Property mode set to 100644
File size: 4.2 KB
Line 
1"""
2Mixture model
3-------------
4
5The product model multiplies the structure factor by the form factor,
6modulated by the effective radius of the form.  The resulting model
7has a attributes of both the model description (with parameters, etc.)
8and the module evaluator (with call, release, etc.).
9
10To use it, first load form factor P and structure factor S, then create
11*ProductModel(P, S)*.
12"""
13from copy import copy
14import numpy as np  # type: ignore
15
16from .modelinfo import Parameter, ParameterTable, ModelInfo
17from .kernel import KernelModel, Kernel
18
19try:
20    from typing import List
21    from .details import CallDetails
22except ImportError:
23    pass
24
25def make_mixture_info(parts):
26    # type: (List[ModelInfo]) -> ModelInfo
27    """
28    Create info block for product model.
29    """
30    flatten = []
31    for part in parts:
32        if part.composition and part.composition[0] == 'mixture':
33            flatten.extend(part.composition[1])
34        else:
35            flatten.append(part)
36    parts = flatten
37
38    # Build new parameter list
39    combined_pars = []
40    for k, part in enumerate(parts):
41        # Parameter prefix per model, A_, B_, ...
42        # Note that prefix must also be applied to id and length_control
43        # to support vector parameters
44        prefix = chr(ord('A')+k) + '_'
45        combined_pars.append(Parameter(prefix+'scale'))
46        for p in part.parameters.kernel_parameters:
47            p = copy(p)
48            p.name = prefix + p.name
49            p.id = prefix + p.id
50            if p.length_control is not None:
51                p.length_control = prefix + p.length_control
52            combined_pars.append(p)
53    parameters = ParameterTable(combined_pars)
54
55    model_info = ModelInfo()
56    model_info.id = '+'.join(part.id for part in parts)
57    model_info.name = ' + '.join(part.name for part in parts)
58    model_info.filename = None
59    model_info.title = 'Mixture model with ' + model_info.name
60    model_info.description = model_info.title
61    model_info.docs = model_info.title
62    model_info.category = "custom"
63    model_info.parameters = parameters
64    #model_info.single = any(part['single'] for part in parts)
65    model_info.structure_factor = False
66    model_info.variant_info = None
67    #model_info.tests = []
68    #model_info.source = []
69    # Iq, Iqxy, form_volume, ER, VR and sesans
70    # Remember the component info blocks so we can build the model
71    model_info.composition = ('mixture', parts)
72
73
74class MixtureModel(KernelModel):
75    def __init__(self, model_info, parts):
76        # type: (ModelInfo, List[KernelModel]) -> None
77        self.info = model_info
78        self.parts = parts
79
80    def __call__(self, q_vectors):
81        # type: (List[np.ndarray]) -> MixtureKernel
82        # Note: may be sending the q_vectors to the n times even though they
83        # are only needed once.  It would mess up modularity quite a bit to
84        # handle this optimally, especially since there are many cases where
85        # separate q vectors are needed (e.g., form in python and structure
86        # in opencl; or both in opencl, but one in single precision and the
87        # other in double precision).
88        kernels = [part.make_kernel(q_vectors) for part in self.parts]
89        return MixtureKernel(self.info, kernels)
90
91    def release(self):
92        # type: () -> None
93        """
94        Free resources associated with the model.
95        """
96        for part in self.parts:
97            part.release()
98
99
100class MixtureKernel(Kernel):
101    def __init__(self, model_info, kernels):
102        # type: (ModelInfo, List[Kernel]) -> None
103        self.dim = kernels[0].dim
104        self.info =  model_info
105        self.kernels = kernels
106
107    def __call__(self, call_details, value, weight, cutoff):
108        # type: (CallDetails, np.ndarray, np.ndarry, float) -> np.ndarray
109        scale, background = value[0:2]
110        total = 0.0
111        # remember the parts for plotting later
112        self.results = []
113        for kernel, kernel_details in zip(self.kernels, call_details.parts):
114            part_result = kernel(kernel_details, value, weight, cutoff)
115            total += part_result
116            self.results.append(part_result)
117
118        return scale*total + background
119
120    def release(self):
121        # type: () -> None
122        for k in self.kernels:
123            k.release()
124
Note: See TracBrowser for help on using the repository browser.