Changeset e44432d in sasmodels for sasmodels/kernel.py
- Timestamp:
- Oct 19, 2018 3:46:26 PM (6 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- be43e39
- Parents:
- 2586ab72
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/kernel.py
ree60aa7 re44432d 43 43 def Iq(self, call_details, values, cutoff, magnetic): 44 44 # type: (CallDetails, np.ndarray, np.ndarray, float, bool) -> np.ndarray 45 Pq, Reff = self.Pq_Reff(call_details, values, cutoff, magnetic, effective_radius_type=0) 46 return Pq 45 r""" 46 Returns I(q) from the polydisperse average scattering. 47 48 .. math:: 49 50 I(q) = \text{scale} \cdot P(q) + \text{background} 51 52 With the correct choice of model and contrast, setting *scale* to 53 the volume fraction $V_f$ of particles should match the measured 54 absolute scattering. Some models (e.g., vesicle) have volume fraction 55 built into the model, and do not need an additional scale. 56 """ 57 _, F2, _, shell_volume, _ = self.Fq(call_details, values, cutoff, magnetic, 58 effective_radius_type=0) 59 combined_scale = values[0]/shell_volume 60 background = values[1] 61 return combined_scale*F2 + background 47 62 __call__ = Iq 48 63 49 def Pq_Reff(self, call_details, values, cutoff, magnetic, effective_radius_type):64 def Fq(self, call_details, values, cutoff, magnetic, effective_radius_type=0): 50 65 # type: (CallDetails, np.ndarray, np.ndarray, float, bool, int) -> np.ndarray 66 r""" 67 Returns <F(q)>, <F(q)^2>, effective radius, shell volume and 68 form:shell volume ratio. The <F(q)> term may be None if the 69 form factor does not support direct computation of $F(q)$ 70 71 $P(q) = <F^2(q)>/<V>$ is used for structure factor calculations, 72 73 .. math:: 74 75 I(q) = \text{scale} \cdot P(q) \cdot S(q) + \text{background} 76 77 For the beta approximation, this becomes 78 79 .. math:: 80 81 I(q) = \text{scale} * P (1 + <F>^2/<F^2> (S - 1)) + \text{background} 82 = \text{scale}/<V> (<F^2> + <F>^2 (S - 1)) + \text{background} 83 84 $<F(q)>$ and $<F^2(q)>$ are averaged by polydispersity in shape 85 and orientation, with each configuration $x_k$ having form factor 86 $F(q, x_k)$, weight $w_k$ and volume $V_k$. The result is: 87 88 .. math:: 89 90 P(q) = \frac{\sum w_k F^2(q, x_k) / \sum w_k}{\sum w_k V_k / \sum w_k} 91 92 The form factor itself is scaled by volume and contrast to compute the 93 total scattering. This is then squared, and the volume weighted 94 F^2 is then normalized by volume F. For a given density, the number 95 of scattering centers is assumed to scale linearly with volume. Later 96 scaling the resulting $P(q)$ by the volume fraction of particles 97 gives the total scattering on an absolute scale. Most models 98 incorporate the volume fraction into the overall scale parameter. An 99 exception is vesicle, which includes the volume fraction parameter in 100 the model itself, scaling $F$ by $\surd V_f$ so that the math for 101 the beta approximation works out. 102 103 By scaling $P(q)$ by total weight $\sum w_k$, there is no need to make 104 sure that the polydisperisity distributions normalize to one. In 105 particular, any distibution values $x_k$ outside the valid domain 106 of $F$ will not be included, and the distribution will be implicitly 107 truncated. This is controlled by the parameter limits defined in the 108 model (which truncate the distribution before calling the kernel) as 109 well as any region excluded using the *INVALID* macro defined within 110 the model itself. 111 112 The volume used in the polydispersity calculation is the form volume 113 for solid objects or the shell volume for hollow objects. Shell 114 volume should be used within $F$ so that the normalizing scale 115 represents the volume fraction of the shell rather than the entire 116 form. This corresponds to the volume fraction of shell-forming 117 material added to the solvent. 118 119 The calculation of $S$ requires the effective radius and the 120 volume fraction of the particles. The model can have several 121 different ways to compute effective radius, with the 122 *effective_radius_type* parameter used to select amongst them. The 123 volume fraction of particles should be determined from the total 124 volume fraction of the form, not just the shell volume fraction. 125 This makes a difference for hollow shapes, which need to scale 126 the volume fraction by the returned volume ratio when computing $S$. 127 For solid objects, the shell volume is set to the form volume so 128 this scale factor evaluates to one and so can be used for both 129 hollow and solid shapes. 130 """ 51 131 self._call_kernel(call_details, values, cutoff, magnetic, effective_radius_type) 52 132 #print("returned",self.q_input.q, self.result) … … 55 135 if total_weight == 0.: 56 136 total_weight = 1. 57 weighted_volume = self.result[nout*self.q_input.nq + 1] 58 weighted_radius = self.result[nout*self.q_input.nq + 2] 59 effective_radius = weighted_radius/total_weight 60 # compute I = scale*P + background 61 # = scale*(sum(w*F^2)/sum w)/(sum (w*V)/sum w) + background 62 # = scale/sum (w*V) * sum(w*F^2) + background 63 F2 = self.result[0:nout*self.q_input.nq:nout] 64 scale = values[0]/(weighted_volume if weighted_volume != 0.0 else 1.0) 65 background = values[1] 66 Pq = scale*F2 + background 67 #print("scale",scale,background) 68 return Pq, effective_radius 69 70 def beta(self, call_details, values, cutoff, magnetic, effective_radius_type): 71 # type: (CallDetails, np.ndarray, np.ndarray, float, bool, int) -> np.ndarray 72 if self.dim == '2d': 73 raise NotImplementedError("beta not yet supported for 2D") 74 if not self.info.have_Fq: 75 raise NotImplementedError("beta not yet supported for "+self.info.id) 76 self._call_kernel(call_details, values, cutoff, magnetic, effective_radius_type) 77 total_weight = self.result[2*self.q_input.nq + 0] 78 if total_weight == 0.: 79 total_weight = 1. 80 weighted_volume = self.result[2*self.q_input.nq + 1] 81 weighted_radius = self.result[2*self.q_input.nq + 2] 82 volume_average = weighted_volume/total_weight 83 effective_radius = weighted_radius/total_weight 84 F2 = self.result[0:2*self.q_input.nq:2]/total_weight 85 F1 = self.result[1:2*self.q_input.nq:2]/total_weight 86 return F1, F2, volume_average, effective_radius 137 # Note: shell_volume == form_volume for solid objects 138 form_volume = self.result[nout*self.q_input.nq + 1]/total_weight 139 shell_volume = self.result[nout*self.q_input.nq + 2]/total_weight 140 effective_radius = self.result[nout*self.q_input.nq + 3]/total_weight 141 if shell_volume == 0.: 142 shell_volume = 1. 143 F1 = self.result[1:nout*self.q_input.nq:nout]/total_weight if nout == 2 else None 144 F2 = self.result[0:nout*self.q_input.nq:nout]/total_weight 145 return F1, F2, effective_radius, shell_volume, form_volume/shell_volume 87 146 88 147 def release(self): … … 92 151 def _call_kernel(self, call_details, values, cutoff, magnetic, effective_radius_type): 93 152 # type: (CallDetails, np.ndarray, np.ndarray, float, bool, int) -> np.ndarray 153 """ 154 Call the kernel. Subclasses defining kernels for particular execution 155 engines need to provide an implementation for this. 156 """ 94 157 raise NotImplementedError()
Note: See TracChangeset
for help on using the changeset viewer.