Changeset bde38b5 in sasmodels for sasmodels/details.py
- Timestamp:
- Aug 14, 2016 11:12:40 PM (8 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- ac98886
- Parents:
- b1c40bee
- git-author:
- Paul Kienzle <pkienzle@…> (08/14/16 23:08:59)
- git-committer:
- Paul Kienzle <pkienzle@…> (08/14/16 23:12:40)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/details.py
r40a87fa rbde38b5 71 71 # pd_offset[max_pd] offset of pd values in parameter array 72 72 # pd_stride[max_pd] index of pd value in loop = n//stride[k] 73 # pd_prodtotal length of pd loop74 # pd_sumtotal length of the weight vector73 # num_eval total length of pd loop 74 # num_weights total length of the weight vector 75 75 # num_active number of pd params 76 76 # theta_par parameter number for theta parameter 77 self.buffer = np. zeros(4*max_pd + 4, 'i4')77 self.buffer = np.empty(4*max_pd + 4, 'i4') 78 78 79 79 # generate views on different parts of the array … … 86 86 self.theta_par = parameters.theta_offset 87 87 88 # offset and length are for all parameters, not just pd parameters 89 # They are not sent to the kernel function, though they could be. 90 # They are used by the composite models (sum and product) to 91 # figure out offsets into the combined value list. 92 self.offset = None # type: np.ndarray 93 self.length = None # type: np.ndarray 94 95 # keep hold of ifno show() so we can break a values vector 96 # into the individual components 97 self.info = model_info 98 88 99 @property 89 100 def pd_par(self): … … 107 118 108 119 @property 109 def pd_prod(self):120 def num_eval(self): 110 121 """Total size of the pd mesh""" 111 122 return self.buffer[-4] 112 123 113 @ pd_prod.setter114 def pd_prod(self, v):124 @num_eval.setter 125 def num_eval(self, v): 115 126 """Total size of the pd mesh""" 116 127 self.buffer[-4] = v 117 128 118 129 @property 119 def pd_sum(self):130 def num_weights(self): 120 131 """Total length of all the weight vectors""" 121 132 return self.buffer[-3] 122 133 123 @ pd_sum.setter124 def pd_sum(self, v):134 @num_weights.setter 135 def num_weights(self, v): 125 136 """Total length of all the weight vectors""" 126 137 self.buffer[-3] = v … … 146 157 self.buffer[-1] = v 147 158 148 def show(self ):159 def show(self, values=None): 149 160 """Print the polydispersity call details to the console""" 150 print("num_active", self.num_active) 151 print("pd_prod", self.pd_prod) 152 print("pd_sum", self.pd_sum) 153 print("theta par", self.theta_par) 154 print("pd_par", self.pd_par) 155 print("pd_length", self.pd_length) 156 print("pd_offset", self.pd_offset) 157 print("pd_stride", self.pd_stride) 158 159 160 def mono_details(model_info): 161 # type: (ModelInfo) -> CallDetails 162 """ 163 Return a :class:`CallDetails` object for a monodisperse calculation 164 of the model defined by *model_info*. 165 """ 166 call_details = CallDetails(model_info) 167 call_details.pd_prod = 1 168 call_details.pd_sum = model_info.parameters.nvalues 169 call_details.pd_par[:] = np.arange(0, model_info.parameters.max_pd) 170 call_details.pd_length[:] = 1 171 call_details.pd_offset[:] = np.arange(0, model_info.parameters.max_pd) 172 call_details.pd_stride[:] = 1 173 return call_details 174 175 176 def poly_details(model_info, weights): 161 print("===== %s details ===="%self.info.name) 162 print("num_active:%d num_eval:%d num_weights:%d theta=%d" 163 % (self.num_active, self.num_eval, self.num_weights, self.theta_par)) 164 if self.pd_par.size: 165 print("pd_par", self.pd_par) 166 print("pd_length", self.pd_length) 167 print("pd_offset", self.pd_offset) 168 print("pd_stride", self.pd_stride) 169 if values is not None: 170 nvalues = self.info.parameters.nvalues 171 print("scale, background", values[:2]) 172 print("val", values[2:nvalues]) 173 print("pd", values[nvalues:nvalues+self.num_weights]) 174 print("wt", values[nvalues+self.num_weights:nvalues+2*self.num_weights]) 175 print("offsets", self.offset) 176 177 178 def make_details(model_info, length, offset, num_weights): 177 179 """ 178 180 Return a :class:`CallDetails` object for a polydisperse calculation 179 of the model defined by *model_info* for the given set of *weights*. 180 *weights* is a list of vectors, one for each parameter. Monodisperse 181 parameters should provide a weight vector containing [1.0]. 182 """ 183 # type: (ModelInfo) -> CallDetails 184 #print("weights",weights) 185 #weights = weights[2:] # Skip scale and background 186 187 # Decreasing list of polydispersity lengths 188 #print([p.id for p in model_info.parameters.call_parameters]) 189 pd_length = np.array([len(w) 190 for w in weights[2:2+model_info.parameters.npars]]) 191 num_active = np.sum(pd_length > 1) 181 of the model defined by *model_info*. Polydispersity is defined by 182 the *length* of the polydispersity distribution for each parameter 183 and the *offset* of the distribution in the polydispersity array. 184 Monodisperse parameters should use a polydispersity length of one 185 with weight 1.0. *num_weights* is the total length of the polydispersity 186 array. 187 """ 188 # type: (ModelInfo, np.ndarray, np.ndarray, int) -> CallDetails 189 #pars = model_info.parameters.call_parameters[2:model_info.parameters.npars+2] 190 #print(", ".join(str(i)+"-"+p.id for i,p in enumerate(pars))) 191 #print("len:",length) 192 #print("off:",offset) 193 194 # Check that we arn't using too many polydispersity loops 195 num_active = np.sum(length > 1) 192 196 max_pd = model_info.parameters.max_pd 193 197 if num_active > max_pd: 194 198 raise ValueError("Too many polydisperse parameters") 195 199 196 pd_offset = np.cumsum(np.hstack((0, pd_length))) 197 #print(", ".join(str(i)+"-"+p.id for i,p in enumerate(model_info.parameters.call_parameters))) 198 #print("len:",pd_length) 199 #print("off:",pd_offset) 200 # Decreasing list of polydpersity lengths 200 201 # Note: the reversing view, x[::-1], does not require a copy 201 idx = np.argsort( pd_length)[::-1][:max_pd]202 pd_stride = np.cumprod(np.hstack((1, pd_length[idx])))202 idx = np.argsort(length)[::-1][:max_pd] 203 pd_stride = np.cumprod(np.hstack((1, length[idx]))) 203 204 204 205 call_details = CallDetails(model_info) 205 206 call_details.pd_par[:max_pd] = idx 206 call_details.pd_length[:max_pd] = pd_length[idx]207 call_details.pd_offset[:max_pd] = pd_offset[idx]207 call_details.pd_length[:max_pd] = length[idx] 208 call_details.pd_offset[:max_pd] = offset[idx] 208 209 call_details.pd_stride[:max_pd] = pd_stride[:-1] 209 call_details. pd_prod= pd_stride[-1]210 call_details. pd_sum = sum(len(w) for w in weights)210 call_details.num_eval = pd_stride[-1] 211 call_details.num_weights = num_weights 211 212 call_details.num_active = num_active 213 call_details.length = length 214 call_details.offset = offset 212 215 #call_details.show() 213 216 return call_details 217 218 219 ZEROS = tuple([0.]*31) 220 def make_kernel_args(kernel, pairs): 221 # type: (Kernel, Tuple[List[np.ndarray], List[np.ndarray]]) -> Tuple[CallDetails, np.ndarray, bool] 222 """ 223 Converts (value, weight) pairs into parameters for the kernel call. 224 225 Returns a CallDetails object indicating the polydispersity, a data object 226 containing the different values, and the magnetic flag indicating whether 227 any magnetic magnitudes are non-zero. Magnetic vectors (M0, phi, theta) are 228 converted to rectangular coordinates (mx, my, mz). 229 """ 230 npars = kernel.info.parameters.npars 231 nvalues = kernel.info.parameters.nvalues 232 scalars = [v[0][0] for v in pairs] 233 values, weights = zip(*pairs[2:npars+2]) if npars else ((),()) 234 length = np.array([len(w) for w in weights]) 235 offset = np.cumsum(np.hstack((0, length))) 236 call_details = make_details(kernel.info, length, offset[:-1], offset[-1]) 237 # Pad value array to a 32 value boundaryd 238 data_len = nvalues + 2*sum(len(v) for v in values) 239 extra = (32 - data_len%32)%32 240 data = np.hstack((scalars,) + values + weights + ZEROS[:extra]) 241 data = data.astype(kernel.dtype) 242 is_magnetic = convert_magnetism(kernel.info.parameters, data) 243 #call_details.show() 244 return call_details, data, is_magnetic 245 246 247 def convert_magnetism(parameters, values): 248 """ 249 Convert magnetism values from polar to rectangular coordinates. 250 251 Returns True if any magnetism is present. 252 """ 253 mag = values[parameters.nvalues-3*parameters.nmagnetic:parameters.nvalues] 254 mag = mag.reshape(-1, 3) 255 scale = mag[:,0] 256 if np.any(scale): 257 theta, phi = mag[:, 1]*pi/180., mag[:, 2]*pi/180. 258 cos_theta = cos(theta) 259 mag[:, 0] = scale*cos_theta*cos(phi) # mx 260 mag[:, 1] = scale*sin(theta) # my 261 mag[:, 2] = -scale*cos_theta*sin(phi) # mz 262 return True 263 else: 264 return False 214 265 215 266 … … 239 290 value = pars 240 291 return value, weight 241 242 243 244 def build_details(kernel, pairs):245 # type: (Kernel, Tuple[List[np.ndarray], List[np.ndarray]]) -> Tuple[CallDetails, np.ndarray, bool]246 """247 Converts (value, weight) pairs into parameters for the kernel call.248 249 Returns a CallDetails object indicating the polydispersity, a data object250 containing the different values, and the magnetic flag indicating whether251 any magnetic magnitudes are non-zero. Magnetic vectors (M0, phi, theta) are252 converted to rectangular coordinates (mx, my, mz).253 """254 values, weights = zip(*pairs)255 scalars = [v[0] for v in values]256 if all(len(w) == 1 for w in weights):257 call_details = mono_details(kernel.info)258 # Pad value array to a 32 value boundary259 data_len = 3*len(scalars)260 extra = ((data_len+31)//32)*32 - data_len261 data = np.array(scalars+scalars+[1.]*len(scalars)+[0.]*extra,262 dtype=kernel.dtype)263 else:264 call_details = poly_details(kernel.info, weights)265 # Pad value array to a 32 value boundary266 data_len = len(scalars) + 2*sum(len(v) for v in values)267 extra = ((data_len+31)//32)*32 - data_len268 data = np.hstack(scalars+list(values)+list(weights)+[0.]*extra)269 data = data.astype(kernel.dtype)270 is_magnetic = convert_magnetism(kernel.info.parameters, data)271 #call_details.show()272 return call_details, data, is_magnetic273 274 def convert_magnetism(parameters, values):275 """276 Convert magnetism values from polar to rectangular coordinates.277 278 Returns True if any magnetism is present.279 """280 mag = values[parameters.nvalues-3*parameters.nmagnetic:parameters.nvalues]281 mag = mag.reshape(-1, 3)282 scale = mag[:,0]283 if np.any(scale):284 theta, phi = mag[:, 1]*pi/180., mag[:, 2]*pi/180.285 cos_theta = cos(theta)286 mag[:, 0] = scale*cos_theta*cos(phi) # mx287 mag[:, 1] = scale*sin(theta) # my288 mag[:, 2] = -scale*cos_theta*sin(phi) # mz289 return True290 else:291 return False
Note: See TracChangeset
for help on using the changeset viewer.