Changeset eeb772e in sasmodels
- Timestamp:
- Apr 1, 2019 10:54:03 AM (6 years ago)
- Parents:
- 3448301 (diff), 7eb2a4d (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent. - git-author:
- Paul Kienzle <pkienzle@…> (04/01/19 10:54:03)
- git-committer:
- GitHub <noreply@…> (04/01/19 10:54:03)
- Files:
-
- 6 added
- 17 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/mixture.py
rb297ba9 rb2f0e5d 117 117 combined_pars.append(p) 118 118 parameters = ParameterTable(combined_pars) 119 # Allow for the scenario in which each component has all its PD parameters 120 # active simultaneously. details.make_details() will throw an error if 121 # too many are used from any one component. 119 122 parameters.max_pd = sum(part.parameters.max_pd for part in parts) 120 123 -
sasmodels/modelinfo.py
ra34b811 r7eb2a4d 70 70 processed.append(parse_parameter(*p)) 71 71 partable = ParameterTable(processed) 72 partable.check_angles( )72 partable.check_angles(strict=True) 73 73 return partable 74 74 … … 446 446 # properties, such as default=0.0 for structure factor backgrounds. 447 447 self.common_parameters = [Parameter(*p) for p in COMMON_PARAMETERS] 448 449 448 self.kernel_parameters = parameters 450 449 self._set_vector_lengths() … … 495 494 self.pd_2d = set(p.name for p in self.call_parameters if p.polydisperse) 496 495 496 # Final checks 497 self.check_duplicates() 498 self.check_angles() 499 497 500 def set_zero_background(self): 498 501 """ … … 506 509 self.defaults = self._get_defaults() 507 510 508 def check_angles(self ):511 def check_angles(self, strict=False): 509 512 """ 510 513 Check that orientation angles are theta, phi and possibly psi. 514 515 *strict* should be True when checking a parameter table defined 516 in a model file, but False when checking from mixture models, etc., 517 where the parameters aren't being passed to a calculator directly. 511 518 """ 512 519 theta = phi = psi = -1 … … 524 531 if p.type != 'orientation': 525 532 raise TypeError("psi must be an orientation parameter") 526 elif p.type == 'orientation':533 elif strict and p.type == 'orientation': 527 534 raise TypeError("only theta, phi and psi can be orientation parameters") 528 535 if theta >= 0 and phi >= 0: … … 532 539 if psi >= 0 and psi != phi+1: 533 540 raise TypeError("psi must follow phi") 534 if (psi >= 0 and psi != last_par) or (psi < 0 and phi != last_par): 541 # TODO: Why must theta/phi/psi be at the end? Consistency only? 542 if strict and phi != last_par and psi != last_par: 535 543 raise TypeError("orientation parameters must appear at the " 536 544 "end of the parameter table") 537 545 elif theta >= 0 or phi >= 0 or psi >= 0: 538 546 raise TypeError("oriented shapes must have both theta and phi and maybe psi") 547 548 def check_duplicates(self): 549 """ 550 Check for duplicate parameter names 551 """ 552 checked, dups = set(), set() 553 for p in self.call_parameters: 554 if p.id in checked: 555 dups.add(p.id) 556 else: 557 checked.add(p.id) 558 if dups: 559 raise TypeError("Duplicate parameters: {}" 560 .format(", ".join(sorted(dups)))) 539 561 540 562 def __getitem__(self, key): -
sasmodels/product.py
r065d77d r93cac17 10 10 To use it, first load form factor P and structure factor S, then create 11 11 *make_product_info(P, S)*. 12 13 The P@S models is somewhat complicated because there are many special 14 parameters that need to be handled in particular ways. Much of the 15 code is used to figure out what special parameters we have, where to 16 find them in the P@S model inputs and how to distribute them to the underlying 17 P and S model calculators. 18 19 The parameter packet received by the P@S is a :class:`details.CallDetails` 20 structure along with a data vector. The CallDetails structure indicates which 21 parameters are polydisperse, the length of the distribution, and where to 22 find it in the data vector. The distributions are ordered from longest to 23 shortest, with length 1 distributions filling out the distribution set. That 24 way the kernel calculator doesn't have to check if it needs another nesting 25 level since it is always there. The data vector consists of a list of target 26 values for the parameters, followed by a concatenation of the distribution 27 values, and then followed by a concatenation of the distribution weights. 28 Given the combined details and data for P@S, we must decompose them in to 29 details for P and details for S separately, which unfortunately requires 30 intimate knowledge of the data structures and tricky code. 31 32 The special parameters are: 33 34 * *scale* and *background*: 35 First two parameters of the value list in each of P, S and P@S. 36 When decomposing P@S parameters, ignore *scale* and *background*, 37 instead using 1 and 0 for the first two slots of both P and S. 38 After calling P and S individually, the results are combined as 39 :code:`volfraction*scale*P*S + background`. The *scale* and 40 *background* do not show up in the polydispersity structure so 41 they are easy to handle. 42 43 * *volfraction*: 44 Always the first parameter of S, but it may also be in P. If it is in P, 45 then *P.volfraction* is used in the combined P@S list, and 46 *S.volfraction* is elided, otherwise *S.volfraction* is used. If we 47 are using *volfraction* from P we can treat it like all the other P 48 parameters when calling P, but when calling S we need to insert the 49 *P.volfraction* into data vector for S and assign a slot of length 1 50 in the distribution. Because we are using the original layout of the 51 distribution vectors from P@S, but copying it into private data 52 vectors for S and P, we are free to "borrow" a P slots to store the 53 missing *S.volfraction* distribution. We use the *P.volfraction* 54 slot itself but any slot will work. 55 56 For hollow shapes, *volfraction* represents the volume fraction of 57 material but S needs the volume fraction enclosed by the shape. The 58 answer is to scale the user specified volume fraction by the form:shell 59 ratio computed from the average form volume and average shell volume 60 returned from P. Use the original *volfraction* divided by *shell_volume* 61 to compute the number density, and scale P@S by that to get absolute 62 scaling on the final *I(q)*. The *scale* for P@S should therefore usually 63 be one. 64 65 * *radius_effective*: 66 Always the second parameter of S and always part of P@S, but never in P. 67 The value may be calculated using *P.radius_effective()* or it 68 may be set to the *radius_effective* value in P@S, depending on 69 *radius_effective_mode*. If part of S, the value may be polydisperse. 70 If calculated by P, then it will be the weighted average of effective 71 radii computed for the polydisperse shape parameters. 72 73 * *structure_factor_mode* 74 If P@S supports beta approximation (i.e., if it has the *Fq* function that 75 returns <FF*> and <F><F*>), then *structure_factor_mode* will be added 76 to the P@S parameters right after the S parameters. This mode may be 0 77 for the monodisperse approximation or 1 for the beta approximation. We 78 will add more values here as we implemented more complicated operations, 79 but for now P and S must be computed separately. If beta, then we 80 return *I = scale volfrac/volume ( <FF> + <F>^2 (S-1)) + background*. 81 If not beta then return *I = scale/volume P S + background* . In both 82 cases, return the appropriate immediate values. 83 84 * *radius_effective_mode* 85 If P defines the *radius_effective* function (and therefore 86 *P.info.radius_effective_modes* is a list of effective radius modes), 87 then *radius_effective_mode* will be the final parameter in P@S. Mode 88 will be zero if *radius_effective* is defined by the user using the S 89 parameter; any other value and the *radius_effective* parameter will be 90 filled in from the value computed in P. In the latter case, the 91 polydispersity information for *S.radius_effective* will need to be 92 suppressed, with pd length set to 1, the first value set to the 93 effective radius and the first weight set to 1. Do this after composing 94 the S data vector so the inputs are left untouched. 95 96 * *regular parameters* 97 The regular P parameters form a block of length *P.info.npars* at the 98 start of the data vector (after scale and background). These will be 99 followed by *S.effective_radius*, and *S.volfraction* (if *P.volfraction* 100 is absent), and then the regular S parameters. The P and S blocks can 101 be copied as a group into the respective P and S data vectors. 102 We can copy the distribution value and weight vectors untouched to both 103 the P and S data vectors since they are referenced by offset and length. 104 We can update the radius_effective slots in the P data vector with 105 *P.radius_effective()* if needed. 106 107 * *magnetic parameters* 108 For each P parameter that is an SLD there will be a set of three magnetic 109 parameters tacked on to P@S after the regular P and S and after the 110 special *structure_factor_mode* and *radius_effective_mode*. These 111 can be copied as a group after the regular P parameters. There won't 112 be any magnetic S parameters. 113 12 114 """ 13 115 from __future__ import print_function, division … … 84 186 if not s_info.parameters.magnetism_index == []: 85 187 raise TypeError("S should not have SLD parameters") 188 if RADIUS_ID in p_info.parameters: 189 raise TypeError("P should not have {}".format(RADIUS_ID)) 86 190 p_id, p_name, p_pars = p_info.id, p_info.name, p_info.parameters 87 191 s_id, s_name, s_pars = s_info.id, s_info.name, s_info.parameters 88 89 # Create list of parameters for the combined model. If there 90 # are any names in P that overlap with those in S, modify the name in S 91 # to distinguish it. 192 p_has_volfrac = VOLFRAC_ID in p_info.parameters 193 194 # Create list of parameters for the combined model. If a name in 195 # S overlaps a name in P, tag the S parameter name to distinguish it. 196 # If the tagged name also collides it will be caught by the parameter 197 # table builder. Similarly if any special names are abused. Need the 198 # pairs to create the translation table for random model generation. 92 199 p_set = set(p.id for p in p_pars.kernel_parameters) 93 s_list = [(_tag_parameter(par) if par.id in p_set else par) 94 for par in s_pars.kernel_parameters] 95 # Check if still a collision after renaming. This could happen if for 96 # example S has volfrac and P has both volfrac and volfrac_S. 97 if any(p.id in p_set for p in s_list): 98 raise TypeError("name collision: P has P.name and P.name_S while S has S.name") 99 100 # make sure effective radius is not a polydisperse parameter in product 101 s_list[0] = copy(s_list[0]) 102 s_list[0].polydisperse = False 103 104 translate_name = dict((old.id, new.id) for old, new 105 in zip(s_pars.kernel_parameters, s_list)) 200 s_pairs = [(par, (_tag_parameter(par) if par.id in p_set else par)) 201 for par in s_pars.kernel_parameters 202 # Note: exclude volfraction from s_list if volfraction in p 203 if par.id != VOLFRAC_ID or not p_has_volfrac] 204 s_list = [pair[0] for pair in s_pairs] 205 206 # Build combined parameter table 106 207 combined_pars = p_pars.kernel_parameters + s_list + make_extra_pars(p_info) 107 208 parameters = ParameterTable(combined_pars) 108 parameters.max_pd = p_pars.max_pd + s_pars.max_pd 209 # Allow for the scenario in which each component has all its PD parameters 210 # active simultaneously. details.make_details() will throw an error if 211 # too many are used from any one component. 212 parameters.Pumax_pd = p_pars.max_pd + s_pars.max_pd 213 214 # TODO: does user-defined polydisperse S.radius_effective make sense? 215 # make sure effective radius is not a polydisperse parameter in product 216 #s_list[0] = copy(s_list[0]) 217 #s_list[0].polydisperse = False 218 219 s_translate = {old.id: new.id for old, new in s_pairs} 109 220 def random(): 110 221 """Random set of model parameters for product model""" 111 222 combined_pars = p_info.random() 112 s_names = set(par.id for par in s_pars.kernel_parameters) 113 combined_pars.update((translate_name[k], v) 223 combined_pars.update((s_translate[k], v) 114 224 for k, v in s_info.random().items() 115 if k in s_ names)225 if k in s_translate) 116 226 return combined_pars 117 227 … … 173 283 174 284 def _intermediates( 175 F 1,# type: np.ndarray176 F 2,# type: np.ndarray285 F, # type: np.ndarray 286 Fsq, # type: np.ndarray 177 287 S, # type: np.ndarray 178 288 scale, # type: float … … 190 300 # TODO: 2. consider implications if there are intermediate results in P(Q) 191 301 parts = OrderedDict(( 192 ("P(Q)", scale*F 2),302 ("P(Q)", scale*Fsq), 193 303 ("S(Q)", S), 194 ("beta(Q)", F 1**2 / F2),195 ("S_eff(Q)", 1 + (F 1**2 / F2)*(S-1)),304 ("beta(Q)", F**2 / Fsq), 305 ("S_eff(Q)", 1 + (F**2 / Fsq)*(S-1)), 196 306 ("effective_radius", radius_effective), 197 307 ("radius_effective", radius_effective), 198 # ("I(Q)", scale*(F 2 + (F1**2)*(S-1)) + bg),308 # ("I(Q)", scale*(Fsq + (F**2)*(S-1)) + bg), 199 309 )) 200 310 else: 201 311 parts = OrderedDict(( 202 ("P(Q)", scale*F 2),312 ("P(Q)", scale*Fsq), 203 313 ("S(Q)", S), 204 314 ("effective_radius", radius_effective), … … 264 374 self.results = [] # type: List[np.ndarray] 265 375 376 # Find index of volfraction parameter in parameter list 377 for k, p in enumerate(model_info.parameters.call_parameters): 378 if p.id == VOLFRAC_ID: 379 self._volfrac_index = k 380 break 381 else: 382 raise RuntimeError("no %s parameter in %s"%(VOLFRAC_ID, self)) 383 384 p_info, s_info = self.info.composition[1] 385 p_npars = p_info.parameters.npars 386 s_npars = s_info.parameters.npars 387 388 have_beta_mode = p_info.have_Fq 389 have_er_mode = p_info.radius_effective_modes is not None 390 volfrac_in_p = self._volfrac_index < p_npars + 2 # scale & background 391 392 # Slices into the details length/offset structure for P@S. 393 # Made complicated by the possibly missing volfraction in S. 394 self._p_detail_slice = slice(0, p_npars) 395 self._s_detail_slice = slice(p_npars, p_npars+s_npars-volfrac_in_p) 396 self._volfrac_in_p = volfrac_in_p 397 398 # P block from data vector, without scale and background 399 first_p = 2 400 last_p = p_npars + 2 401 self._p_value_slice = slice(first_p, last_p) 402 403 # radius_effective is the first parameter in S from the data vector. 404 self._er_index = last_p 405 406 # S block from data vector, without scale, background, volfrac or er. 407 first_s = last_p + 2 - volfrac_in_p 408 last_s = first_s + s_npars - 2 409 self._s_value_slice = slice(first_s, last_s) 410 411 # S distribution block in S data vector starts after all S values 412 self._s_dist_slice = slice(2 + s_npars, None) 413 414 # structure_factor_mode is the first parameter after P and S. Skip 415 # 2 for scale and background, and subtract 1 in case there is no 416 # volfraction in S. 417 self._beta_mode_index = last_s if have_beta_mode else 0 418 419 # radius_effective_mode is the second parameter after P and S 420 # unless structure_factor_mode isn't available, in which case it 421 # is first. 422 self._er_mode_index = last_s + have_beta_mode if have_er_mode else 0 423 424 # Magnetic parameters are after everything else. If they exist, 425 # they will only be for form factor P, not structure factor S. 426 first_mag = last_s + have_beta_mode + have_er_mode 427 mag_pars = 3*p_info.parameters.nmagnetic 428 last_mag = first_mag + (mag_pars + 3 if mag_pars else 0) 429 self._magentic_slice = slice(first_mag, last_mag) 430 266 431 def Iq(self, call_details, values, cutoff, magnetic): 267 432 # type: (CallDetails, np.ndarray, float, bool) -> np.ndarray 268 269 433 p_info, s_info = self.info.composition[1] 270 p_npars = p_info.parameters.npars 271 p_length = call_details.length[:p_npars] 272 p_offset = call_details.offset[:p_npars] 273 s_npars = s_info.parameters.npars 274 s_length = call_details.length[p_npars:p_npars+s_npars] 275 s_offset = call_details.offset[p_npars:p_npars+s_npars] 276 277 # Beta mode parameter is the first parameter after P and S parameters 278 have_beta_mode = p_info.have_Fq 279 beta_mode_offset = 2+p_npars+s_npars 280 beta_mode = (values[beta_mode_offset] > 0) if have_beta_mode else False 281 if beta_mode and self.p_kernel.dim == '2d': 282 raise NotImplementedError("beta not yet supported for 2D") 283 284 # R_eff type parameter is the second parameter after P and S parameters 285 # unless the model doesn't support beta mode, in which case it is first 286 have_radius_type = p_info.radius_effective_modes is not None 287 #print(p_npars,s_npars) 288 radius_type_offset = 2+p_npars+s_npars + (1 if have_beta_mode else 0) 289 #print(values[radius_type_offset]) 290 radius_type = int(values[radius_type_offset]) if have_radius_type else 0 291 292 # Retrieve the volume fraction, which is the second of the 293 # 'S' parameters in the parameter list, or 2+np in 0-origin, 294 # as well as the scale and background. 295 volfrac = values[3+p_npars] 434 435 # Retrieve values from the data vector 296 436 scale, background = values[0], values[1] 297 298 # if there are magnetic parameters, they will only be on the 299 # form factor P, not the structure factor S. 300 nmagnetic = len(self.info.parameters.magnetism_index) 301 if nmagnetic: 302 spin_index = self.info.parameters.npars + 2 303 magnetism = values[spin_index: spin_index+3+3*nmagnetic] 304 else: 305 magnetism = [] 437 volfrac = values[self._volfrac_index] 438 er_mode = (int(values[self._er_mode_index]) 439 if self._er_mode_index > 0 else 0) 440 beta_mode = (values[self._beta_mode_index] > 0 441 if self._beta_mode_index > 0 else False) 442 306 443 nvalues = self.info.parameters.nvalues 307 444 nweights = call_details.num_weights 308 445 weights = values[nvalues:nvalues + 2*nweights] 309 446 447 # Can't do 2d and beta_mode just yet 448 if beta_mode and self.p_kernel.dim == '2d': 449 raise NotImplementedError("beta not yet supported for 2D") 450 310 451 # Construct the calling parameters for P. 452 p_length = call_details.length[self._p_detail_slice] 453 p_offset = call_details.offset[self._p_detail_slice] 311 454 p_details = make_details(p_info, p_length, p_offset, nweights) 312 455 p_values = [ 313 456 [1., 0.], # scale=1, background=0, 314 values[ 2:2+p_npars],315 magnetism,457 values[self._p_value_slice], 458 values[self._magentic_slice], 316 459 weights] 317 460 spacer = (32 - sum(len(v) for v in p_values)%32)%32 … … 319 462 p_values = np.hstack(p_values).astype(self.p_kernel.dtype) 320 463 464 # Call the form factor kernel to compute <F> and <F^2>. 465 # If the model doesn't support Fq the returned <F> will be None. 466 F, Fsq, radius_effective, shell_volume, volume_ratio \ 467 = self.p_kernel.Fq(p_details, p_values, cutoff, magnetic, er_mode) 468 469 # TODO: async call to the GPU 470 321 471 # Construct the calling parameters for S. 322 if radius_type > 0: 323 # If R_eff comes from form factor, make sure it is monodisperse. 324 # weight is set to 1 later, after the value array is created 472 s_length = call_details.length[self._s_detail_slice] 473 s_offset = call_details.offset[self._s_detail_slice] 474 if self._volfrac_in_p: 475 # Volfrac is in P and missing from S so insert a slot for it. Say 476 # the distribution is length 1 and use the slot for volfraction 477 # from the P distribution. 478 s_length = np.insert(s_length, 1, 1) 479 s_offset = np.insert(s_offset, 1, p_offset[self._volfrac_index - 2]) 480 if er_mode > 0: 481 # If effective_radius comes from P, make sure it is monodisperse. 482 # Weight is set to 1 later, after the value array is created 325 483 s_length[0] = 1 326 484 s_details = make_details(s_info, s_length, s_offset, nweights) 327 485 s_values = [ 328 [1., 0.], # scale=1, background=0, 329 values[2+p_npars:2+p_npars+s_npars], 486 [1., # scale=1 487 0., # background=0, 488 values[self._er_index], # S.radius_effective; may be replaced by P 489 0.], # volfraction; will be replaced by volfrac * volume_ratio 490 # followed by S parameters after effective_radius and volfraction 491 values[self._s_value_slice], 330 492 weights, 331 493 ] … … 334 496 s_values = np.hstack(s_values).astype(self.s_kernel.dtype) 335 497 336 # Call the form factor kernel to compute <F> and <F^2>.337 # If the model doesn't support Fq the returned <F> will be None.338 F1, F2, radius_effective, shell_volume, volume_ratio = self.p_kernel.Fq(339 p_details, p_values, cutoff, magnetic, radius_type)340 341 # Call the structure factor kernel to compute S.342 498 # Plug R_eff from the form factor into structure factor parameters 343 499 # and scale volume fraction by form:shell volume ratio. These changes … … 347 503 #print("R_eff=%d:%g, volfrac=%g, volume ratio=%g" 348 504 # % (radius_type, radius_effective, volfrac, volume_ratio)) 349 if radius_type > 0: 505 s_dist = s_values[self._s_dist_slice] 506 if er_mode > 0: 350 507 # set the value to the model R_eff and set the weight to 1 351 s_values[2] = s_values[2+s_npars+s_offset[0]] = radius_effective 352 s_values[2+s_npars+s_offset[0]+nweights] = 1.0 353 s_values[3] = s_values[2+s_npars+s_offset[1]] = volfrac*volume_ratio 508 s_values[2] = s_dist[s_offset[0]] = radius_effective 509 s_dist[s_offset[0]+nweights] = 1.0 510 s_values[3] = s_dist[s_offset[1]] = volfrac*volume_ratio 511 s_dist[s_offset[1]+nweights] = 1.0 512 513 # Call the structure factor kernel to compute S. 354 514 S = self.s_kernel.Iq(s_details, s_values, cutoff, False) 515 #print("P", Fsq[:10]) 516 #print("S", S[:10]) 517 #print(radius_effective, volfrac*volume_ratio) 518 519 # Combine form factor and structure factor 520 #print("beta", beta_mode, F, Fsq, S) 521 PS = Fsq + F**2*(S-1) if beta_mode else Fsq*S 355 522 356 523 # Determine overall scale factor. Hollow shapes are weighted by 357 # shell_volume, so that is needed for volume normalization. For 358 # solid shapes we can use shell_volume as well since it is equal 359 # to form volume. 360 combined_scale = scale*volfrac/shell_volume 361 362 # Combine form factor and structure factor 363 #print("beta", beta_mode, F1, F2, S) 364 PS = F2 + F1**2*(S-1) if beta_mode else F2*S 365 final_result = combined_scale*PS + background 524 # shell_volume, so that is needed for number density estimation. 525 # For solid shapes we can use shell_volume as well since it is 526 # equal to form volume. If P already has a volfraction parameter, 527 # then assume that it is already on absolute scale, and don't 528 # include volfrac in the combined_scale. 529 combined_scale = scale*(volfrac if not self._volfrac_in_p else 1.0) 530 final_result = combined_scale/shell_volume*PS + background 366 531 367 532 # Capture intermediate values so user can see them. These are … … 375 540 # the results directly rather than through a lazy evaluator. 376 541 self.results = lambda: _intermediates( 377 F 1, F2, S, combined_scale, radius_effective, beta_mode)542 F, Fsq, S, combined_scale, radius_effective, beta_mode) 378 543 379 544 return final_result -
doc/guide/index.rst
rda5536f rbc69321 12 12 pd/polydispersity.rst 13 13 resolution.rst 14 plugin.rst 15 fitting_sq.rst 14 16 magnetism/magnetism.rst 15 17 orientation/orientation.rst 16 18 sesans/sans_to_sesans.rst 17 19 sesans/sesans_fitting.rst 18 plugin.rst19 20 scripting.rst 20 21 refs.rst -
doc/guide/pd/polydispersity.rst
rd089a00 ra5cb9bc 11 11 -------------------------------------------- 12 12 13 For some models we can calculate the average intensity for a population of 14 particles that possess size and/or orientational (ie, angular) distributions. 15 In SasView we call the former *polydispersity* but use the parameter *PD* to 16 parameterise both. In other words, the meaning of *PD* in a model depends on 13 For some models we can calculate the average intensity for a population of 14 particles that possess size and/or orientational (ie, angular) distributions. 15 In SasView we call the former *polydispersity* but use the parameter *PD* to 16 parameterise both. In other words, the meaning of *PD* in a model depends on 17 17 the actual parameter it is being applied too. 18 18 19 The resultant intensity is then normalized by the average particle volume such 19 The resultant intensity is then normalized by the average particle volume such 20 20 that 21 21 … … 24 24 P(q) = \text{scale} \langle F^* F \rangle / V + \text{background} 25 25 26 where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an 26 where $F$ is the scattering amplitude and $\langle\cdot\rangle$ denotes an 27 27 average over the distribution $f(x; \bar x, \sigma)$, giving 28 28 29 29 .. math:: 30 30 31 P(q) = \frac{\text{scale}}{V} \int_\mathbb{R} 31 P(q) = \frac{\text{scale}}{V} \int_\mathbb{R} 32 32 f(x; \bar x, \sigma) F^2(q, x)\, dx + \text{background} 33 33 34 34 Each distribution is characterized by a center value $\bar x$ or 35 35 $x_\text{med}$, a width parameter $\sigma$ (note this is *not necessarily* 36 the standard deviation, so read the description of the distribution carefully), 37 the number of sigmas $N_\sigma$ to include from the tails of the distribution, 38 and the number of points used to compute the average. The center of the 39 distribution is set by the value of the model parameter. 40 41 The distribution width applied to *volume* (ie, shape-describing) parameters 42 is relative to the center value such that $\sigma = \mathrm{PD} \cdot \bar x$. 43 However, the distribution width applied to *orientation* parameters is just 44 $\sigma = \mathrm{PD}$. 36 the standard deviation, so read the description carefully), the number of 37 sigmas $N_\sigma$ to include from the tails of the distribution, and the 38 number of points used to compute the average. The center of the distribution 39 is set by the value of the model parameter. The meaning of a polydispersity 40 parameter *PD* (not to be confused with a molecular weight distributions 41 in polymer science) in a model depends on the type of parameter it is being 42 applied too. 43 44 The distribution width applied to *volume* (ie, shape-describing) parameters 45 is relative to the center value such that $\sigma = \mathrm{PD} \cdot \bar x$. 46 However, the distribution width applied to *orientation* (ie, angle-describing) 47 parameters is just $\sigma = \mathrm{PD}$. 45 48 46 49 $N_\sigma$ determines how far into the tails to evaluate the distribution, … … 52 55 53 56 Users should note that the averaging computation is very intensive. Applying 54 polydispersion and/or orientational distributions to multiple parameters at 55 the same time, or increasing the number of points in the distribution, will 56 require patience! However, the calculations are generally more robust with 57 polydispersion and/or orientational distributions to multiple parameters at 58 the same time, or increasing the number of points in the distribution, will 59 require patience! However, the calculations are generally more robust with 57 60 more data points or more angles. 58 61 … … 66 69 * *Schulz Distribution* 67 70 * *Array Distribution* 71 * *User-defined Distributions* 68 72 69 73 These are all implemented as *number-average* distributions. 70 74 71 Additional distributions are under consideration.72 75 73 76 **Beware: when the Polydispersity & Orientational Distribution panel in SasView is** … … 75 78 **This may not be suitable. See Suggested Applications below.** 76 79 77 .. note:: In 2009 IUPAC decided to introduce the new term 'dispersity' to replace 78 the term 'polydispersity' (see `Pure Appl. Chem., (2009), 81(2), 79 351-353 <http://media.iupac.org/publications/pac/2009/pdf/8102x0351.pdf>`_ 80 in order to make the terminology describing distributions of chemical 81 properties unambiguous. However, these terms are unrelated to the 82 proportional size distributions and orientational distributions used in 80 .. note:: In 2009 IUPAC decided to introduce the new term 'dispersity' to replace 81 the term 'polydispersity' (see `Pure Appl. Chem., (2009), 81(2), 82 351-353 <http://media.iupac.org/publications/pac/2009/pdf/8102x0351.pdf>`_ 83 in order to make the terminology describing distributions of chemical 84 properties unambiguous. However, these terms are unrelated to the 85 proportional size distributions and orientational distributions used in 83 86 SasView models. 84 87 … … 92 95 or angular orientations, consider using the Gaussian or Boltzmann distributions. 93 96 94 If applying polydispersion to parameters describing angles, use the Uniform 95 distribution. Beware of using distributions that are always positive (eg, the 97 If applying polydispersion to parameters describing angles, use the Uniform 98 distribution. Beware of using distributions that are always positive (eg, the 96 99 Lognormal) because angles can be negative! 97 100 98 The array distribution allows a user-defined distribution to be applied. 101 The array distribution provides a very simple means of implementing a user- 102 defined distribution, but without any fittable parameters. Greater flexibility 103 is conferred by the user-defined distribution. 99 104 100 105 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ … … 334 339 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 335 340 341 User-defined Distributions 342 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 343 344 You can also define your own distribution by creating a python file defining a 345 *Distribution* object with a *_weights* method. The *_weights* method takes 346 *center*, *sigma*, *lb* and *ub* as arguments, and can access *self.npts* 347 and *self.nsigmas* from the distribution. They are interpreted as follows: 348 349 * *center* the value of the shape parameter (for size dispersity) or zero 350 if it is an angular dispersity. This parameter may be fitted. 351 352 * *sigma* the width of the distribution, which is the polydispersity parameter 353 times the center for size dispersity, or the polydispersity parameter alone 354 for angular dispersity. This parameter may be fitted. 355 356 * *lb*, *ub* are the parameter limits (lower & upper bounds) given in the model 357 definition file. For example, a radius parameter has *lb* equal to zero. A 358 volume fraction parameter would have *lb* equal to zero and *ub* equal to one. 359 360 * *self.nsigmas* the distance to go into the tails when evaluating the 361 distribution. For a two parameter distribution, this value could be 362 co-opted to use for the second parameter, though it will not be available 363 for fitting. 364 365 * *self.npts* the number of points to use when evaluating the distribution. 366 The user will adjust this to trade calculation time for accuracy, but the 367 distribution code is free to return more or fewer, or use it for the third 368 parameter in a three parameter distribution. 369 370 As an example, the code following wraps the Laplace distribution from scipy stats:: 371 372 import numpy as np 373 from scipy.stats import laplace 374 375 from sasmodels import weights 376 377 class Dispersion(weights.Dispersion): 378 r""" 379 Laplace distribution 380 381 .. math:: 382 383 w(x) = e^{-\sigma |x - \mu|} 384 """ 385 type = "laplace" 386 default = dict(npts=35, width=0, nsigmas=3) # default values 387 def _weights(self, center, sigma, lb, ub): 388 x = self._linspace(center, sigma, lb, ub) 389 wx = laplace.pdf(x, center, sigma) 390 return x, wx 391 392 You can plot the weights for a given value and width using the following:: 393 394 from numpy import inf 395 from matplotlib import pyplot as plt 396 from sasmodels import weights 397 398 # reload the user-defined weights 399 weights.load_weights() 400 x, wx = weights.get_weights('laplace', n=35, width=0.1, nsigmas=3, value=50, 401 limits=[0, inf], relative=True) 402 403 # plot the weights 404 plt.interactive(True) 405 plt.plot(x, wx, 'x') 406 407 The *self.nsigmas* and *self.npts* parameters are normally used to control 408 the accuracy of the distribution integral. The *self._linspace* function 409 uses them to define the *x* values (along with the *center*, *sigma*, 410 *lb*, and *ub* which are passed as parameters). If you repurpose npts or 411 nsigmas you will need to generate your own *x*. Be sure to honour the 412 limits *lb* and *ub*, for example to disallow a negative radius or constrain 413 the volume fraction to lie between zero and one. 414 415 To activate a user-defined distribution, put it in a file such as *distname.py* 416 in the *SAS_WEIGHTS_PATH* folder. This is defined with an environment 417 variable, defaulting to:: 418 419 SAS_WEIGHTS_PATH=~/.sasview/weights 420 421 The weights path is loaded on startup. To update the distribution definition 422 in a running application you will need to enter the following python commands:: 423 424 import sasmodels.weights 425 sasmodels.weights.load_weights('path/to/distname.py') 426 427 .. ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ 428 336 429 Note about DLS polydispersity 337 430 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 338 431 339 Several measures of polydispersity abound in Dynamic Light Scattering (DLS) and 340 it should not be assumed that any of the following can be simply equated with 432 Several measures of polydispersity abound in Dynamic Light Scattering (DLS) and 433 it should not be assumed that any of the following can be simply equated with 341 434 the polydispersity *PD* parameter used in SasView. 342 435 343 The dimensionless **Polydispersity Index (PI)** is a measure of the width of the 344 distribution of autocorrelation function decay rates (*not* the distribution of 345 particle sizes itself, though the two are inversely related) and is defined by 436 The dimensionless **Polydispersity Index (PI)** is a measure of the width of the 437 distribution of autocorrelation function decay rates (*not* the distribution of 438 particle sizes itself, though the two are inversely related) and is defined by 346 439 ISO 22412:2017 as 347 440 … … 350 443 PI = \mu_{2} / \bar \Gamma^2 351 444 352 where $\mu_\text{2}$ is the second cumulant, and $\bar \Gamma^2$ is the 445 where $\mu_\text{2}$ is the second cumulant, and $\bar \Gamma^2$ is the 353 446 intensity-weighted average value, of the distribution of decay rates. 354 447 … … 359 452 PI = \sigma^2 / 2\bar \Gamma^2 360 453 361 where $\sigma$ is the standard deviation, allowing a **Relative Polydispersity (RP)** 454 where $\sigma$ is the standard deviation, allowing a **Relative Polydispersity (RP)** 362 455 to be defined as 363 456 … … 366 459 RP = \sigma / \bar \Gamma = \sqrt{2 \cdot PI} 367 460 368 PI values smaller than 0.05 indicate a highly monodisperse system. Values 461 PI values smaller than 0.05 indicate a highly monodisperse system. Values 369 462 greater than 0.7 indicate significant polydispersity. 370 463 371 The **size polydispersity P-parameter** is defined as the relative standard 372 deviation coefficient of variation 464 The **size polydispersity P-parameter** is defined as the relative standard 465 deviation coefficient of variation 373 466 374 467 .. math:: … … 377 470 378 471 where $\nu$ is the variance of the distribution and $\bar R$ is the mean 379 value of $R$. Here, the product $P \bar R$ is *equal* to the standard 472 value of $R$. Here, the product $P \bar R$ is *equal* to the standard 380 473 deviation of the Lognormal distribution. 381 474 -
doc/guide/plugin.rst
rdb1d9d5 re15a822 456 456 ............. 457 457 458 .. note:: 459 460 Pure python models do not yet support direct computation of $<F(Q)^2>$ or 461 $<F(Q)>^2$. Neither do they support orientational distributions or magnetism 462 (use C models if these are required). 463 458 464 For pure python models, define the *Iq* function:: 459 465 … … 516 522 is automatically scaled by *form_volume/shell_volume* prior to calling the 517 523 structure factor. 518 519 **Note: Pure python models do not yet support direct computation of the**520 **average of $F(q)$ and $F^2(q)$. Neither do they support orientational**521 **distributions or magnetism (use C models if these are required).**522 524 523 525 Embedded C Models -
sasmodels/compare.py
rb297ba9 rd0b0f5d 39 39 40 40 from . import core 41 from . import weights 41 42 from . import kerneldll 42 43 from . import kernelcl … … 45 46 from .direct_model import DirectModel, get_mesh 46 47 from .generate import FLOAT_RE, set_integration_size 47 from .weights import plot_weights48 48 49 49 # pylint: disable=unused-import … … 115 115 116 116 === environment variables === 117 -DSAS_MODELPATH=path sets directory containing custom models 117 -DSAS_MODELPATH=~/.sasmodels/custom_models sets path to custom models 118 -DSAS_WEIGHTS_PATH=~/.sasview/weights sets path to custom distributions 118 119 -DSAS_OPENCL=vendor:device|cuda:device|none sets the target GPU device 119 120 -DXDG_CACHE_HOME=~/.cache sets the pyopencl cache root (linux only) 120 121 -DSAS_COMPILER=tinycc|msvc|mingw|unix sets the DLL compiler 121 -DSAS_OPENMP= 1 turnson OpenMP for the DLLs122 -DSAS_DLL_PATH= path sets the path to the compiled modules122 -DSAS_OPENMP=0 set to 1 to turn on OpenMP for the DLLs 123 -DSAS_DLL_PATH=~/.sasmodels/compiled_models sets the DLL cache 123 124 124 125 The interpretation of quad precision depends on architecture, and may … … 784 785 model_info = base._kernel.info 785 786 dim = base._kernel.dim 786 plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim))787 weights.plot_weights(model_info, get_mesh(model_info, base_pars, dim=dim)) 787 788 if opts['show_profile']: 788 789 import pylab … … 1441 1442 #import pprint; pprint.pprint(model_info) 1442 1443 1444 # Hack to load user-defined distributions; run through all parameters 1445 # and make sure any pd_type parameter is a defined distribution. 1446 if (any(p.endswith('pd_type') and v not in weights.MODELS 1447 for p, v in pars.items()) or 1448 any(p.endswith('pd_type') and v not in weights.MODELS 1449 for p, v in pars2.items())): 1450 weights.load_weights() 1451 1443 1452 if opts['show_pars']: 1444 1453 if model_info.name != model_info2.name or pars != pars2: -
sasmodels/kernelcl.py
ra34b811 r9fac5f5 158 158 ENV = None 159 159 def reset_environment(): 160 # type: () -> None161 """ 162 Call to createa new OpenCL context, such as after a change to SAS_OPENCL.160 # type: () -> "GpuEnvironment" 161 """ 162 Return a new OpenCL context, such as after a change to SAS_OPENCL. 163 163 """ 164 164 global ENV 165 165 ENV = GpuEnvironment() if use_opencl() else None 166 166 return ENV 167 167 168 168 def environment(): -
sasmodels/models/__init__.py
r2d81cfe rd827c5e 1 1 """ 2 1D Modeling for SAS 2 Model definition files 3 ---------------------- 4 5 The models below are grouped by type. The list is a snapshot at a particular 6 time and may be out of date. 7 8 Models with pure form factor (all of which define *F(Q)*): 9 10 :mod:`barbell` 11 :mod:`capped_cylinder` 12 :mod:`core_multi_shell` 13 :mod:`core_shell_bicelle` 14 :mod:`core_shell_bicelle_elliptical` 15 :mod:`core_shell_bicelle_elliptical_belt_rough` 16 :mod:`core_shell_cylinder` 17 :mod:`core_shell_ellipsoid` 18 :mod:`core_shell_parallelepipied` 19 :mod:`core_shell_sphere` 20 :mod:`cylinder` [limiting conditions (long rods, thin disks) not implemented] 21 :mod:`ellipsoid` 22 :mod:`elliptical_cylinder` 23 :mod:`fuzzy_sphere` 24 :mod:`hollow_cylinder` 25 :mod:`hollow_rectangular_prism` 26 :mod:`hollow_rectangular_prism_thin_walls` 27 :mod:`multilayer_vesicle` 28 :mod:`onion` 29 :mod:`parallelepiped` 30 :mod:`rectangular_prism` 31 :mod:`sphere` 32 :mod:`spherical_sld` 33 :mod:`triaxial_ellipsoid` 34 :mod:`vesicle` 35 36 Models with local structure factor: 37 38 :mod:`flexible_cylinder` 39 :mod:`flexible_cylinder_elliptical` 40 :mod:`linear_pearls` 41 :mod:`mono_gauss_coil` 42 :mod:`pearl_necklace` 43 :mod:`poly_gauss_coil` 44 :mod:`polymer_micelle` 45 :mod:`pringle` 46 :mod:`raspberry` 47 :mod:`stacked_disks` 48 :mod:`star_polymer` 49 50 Models with long range structure factor: 51 52 :mod:`binary_hard_sphere` 53 :mod:`bcc_paracrystal` 54 :mod:`fcc_paracrystal` 55 :mod:`fractal` 56 :mod:`fractal_core_shell` 57 :mod:`lamellar` 58 :mod:`lamellar_hg` 59 :mod:`lamellar_hg_stack_caille` 60 :mod:`lamellar_stack_caille` 61 :mod:`lamellar_stack_paracrystal` 62 :mod:`mass_fractal` 63 :mod:`mass_surface_fractal` 64 :mod:`rpa` 65 :mod:`sc_paracrystal` 66 :mod:`surface_fractal` 67 68 Models which are pure structure factors:: 69 70 :mod:`hardsphere` 71 :mod:`hayter_msa` 72 :mod:`squarewell` 73 :mod:`stickyhardsphere` 74 75 Other models: 76 77 :mod:`adsorbed_layer` 78 :mod:`be_polyelectrolyte` 79 :mod:`broad_peak` 80 :mod:`correlation_length` 81 :mod:`dab` 82 :mod:`gauss_lorentz_gel` 83 :mod:`gaussian_peak` 84 :mod:`gel_fit` 85 :mod:`guinier_porod` 86 :mod:`guinier` 87 :mod:`line` 88 :mod:`lorentz` 89 :mod:`peak_lorentz` 90 :mod:`polymer_excl_volume` 91 :mod:`porod` 92 :mod:`power_law` 93 :mod:`spinodal` 94 :mod:`teubner_strey` 95 :mod:`two_lorentzian` 96 :mod:`unified_power_Rg` 97 3 98 """ -
sasmodels/models/hardsphere.py
rdb1d9d5 r4d00de6 16 16 Earlier versions of SasView did not incorporate the so-called 17 17 $\beta(q)$ ("beta") correction [1] for polydispersity and non-sphericity. 18 This is only available in SasView versions 4.2.2and higher.18 This is only available in SasView versions 5.0 and higher. 19 19 20 20 radius_effective is the effective hard sphere radius. -
sasmodels/models/hayter_msa.py
rdb1d9d5 r4d00de6 18 18 Earlier versions of SasView did not incorporate the so-called 19 19 $\beta(q)$ ("beta") correction [3] for polydispersity and non-sphericity. 20 This is only available in SasView versions 4.2.2and higher.20 This is only available in SasView versions 5.0 and higher. 21 21 22 22 The salt concentration is used to compute the ionic strength of the solution -
sasmodels/models/sphere.py
ra34b811 r934a001 36 36 References 37 37 ---------- 38 39 .. [#] A Guinier and G. Fournet, *Small-Angle Scattering of X-Rays*, John Wiley and Sons, New York, (1955) 38 39 .. [#] A Guinier and G. Fournet, *Small-Angle Scattering of X-Rays*, 40 John Wiley and Sons, New York, (1955) 40 41 41 42 Source … … 90 91 ) 91 92 return pars 92 93 #2345678901234567890123456789012345678901234567890123456789012345678901234567890 93 94 tests = [ 94 [{}, 0.2, 0.726362], # each test starts with default parameter values inside { }, unless modified. Then Q and expected value of I(Q) 95 [{}, 0.2, 0.726362], # each test starts with default parameter values 96 # inside { }, unless modified. Then Q and expected value of I(Q) 97 # putting None for an expected result will pass the test if there are no 98 # errors from the routine, but without any check on the value of the result 99 [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 100 "radius": 120.}, [0.01,0.1,0.2], 101 [1.34836265e+04, 6.20114062e+00, 1.04733914e-01]], 95 102 [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 103 # careful tests here R=120 Pd=.2, then with S(Q) at default Reff=50 104 # (but this gets changed to 120) phi=0,2 96 105 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 97 0.2, 0.2288431], 98 [{"radius": 120., "radius_pd": 0.02, "radius_pd_n":45}, 99 0.2, 792.0646662454202, 1166737.0473152, 120.0, 7246723.820358589, 1.0], # the longer list here checks F1, F2, R_eff, volume, volume_ratio = call_Fq(kernel, pars) 106 [0.01,0.1,0.2], [1.74395295e+04, 3.68016987e+00, 2.28843099e-01]], 107 # a list of Q values and list of expected results is also possible 108 [{"scale": 1., "background": 0., "sld": 6., "sld_solvent": 1., 109 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 110 0.01, 335839.88055473, 1.41045057e+11, 120.0, 8087664.122641933, 1.0], 111 # the longer list here checks F1, F2, R_eff, volume, volume_ratio 112 [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 113 0.1, 482.93824329, 29763977.79867414, 120.0, 8087664.122641933, 1.0], 114 [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 115 0.2, 1.23330406, 1850806.1197361, 120.0, 8087664.122641933, 1.0], 100 116 # But note P(Q) = F2/volume 101 # F and F^2 are "unscaled", with for n <F F*>S(q) or for beta approx I(q) = n [<F F*> + <F><F*> (S(q) - 1)] 102 # for n the number density and <.> the orientation average, and F = integral rho(r) exp(i q . r) dr. 117 # F and F^2 are "unscaled", with for n <F F*>S(q) or for beta approx 118 # I(q) = n [<F F*> + <F><F*> (S(q) - 1)] 119 # for n the number density and <.> the orientation average, and 120 # F = integral rho(r) exp(i q . r) dr. 103 121 # The number density is volume fraction divided by particle volume. 104 # Effectively, this leaves F = V drho form, where form is the usual 3 j1(qr)/(qr) or whatever depending on the shape. 105 # [{"@S": "hardsphere"}, 106 # 0.01, 55.881884232102124], # this is current value, not verified elsewhere yet 107 # [{"radius": 120., "radius_pd": 0.2, "radius_pd_n":45}, 108 # 0.2, 1.233304061, [1850806.119736], 120.0, 8087664.1226, 1.0], # the longer list here checks F1, F2, R_eff, volume, volume_ratio = call_Fq(kernel, pars) 109 # [{"@S": "hardsphere"}, 110 # 0.2, 0.14730859242492958], # this is current value, not verified elsewhere yet 111 # [{"@S": "hardsphere"}, 112 # 0.1, 0.7940350343811906], # this is current value, not verified elsewhere yet 122 # Effectively, this leaves F = V drho form, where form is the usual 123 # 3 j1(qr)/(qr) or whatever depending on the shape. 124 # @S RESULTS using F1 and F2 from the longer test strng above: 125 # 126 # I(Q) = (F2 + F1^2*(S(Q) -1))*volfraction*scale/Volume + background 127 # 128 # with by default scale=1.0, background=0.001 129 # NOTE currently S(Q) volfraction is also included in scaling 130 # structure_factor_mode 0 = normal decoupling approx, 131 # 1 = beta(Q) approx 132 # radius_effective_mode 0 is for free choice, 133 # 1 is use radius from F2(Q) 134 # (sphere only has two choices, other models may have more) 135 [{"@S": "hardsphere", 136 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45,"volfraction":0.2, 137 #"radius_effective":50.0, # hard sphere structure factor 138 "structure_factor_mode": 1, # mode 0 = normal decoupling approx, 139 # 1 = beta(Q) approx 140 "radius_effective_mode": 0 # this used default hardsphere Reff=50 141 }, [0.01,0.1,0.2], [1.32473756e+03, 7.36633631e-01, 4.67686201e-02] ], 113 142 [{"@S": "hardsphere", 114 143 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 115 144 "volfraction":0.2, 116 "radius_effective":45.0, # uses this (check)117 "structure_factor_mode": 1, # 0 = normal decoupling approximation, 1 = beta(Q)approx118 "radius_effective_mode": 0 # equivalent sphere, there is only one valid mode for sphere. says -this used r_eff =0 or default 50?145 "radius_effective":45.0, # explicit Reff over rides either 50 or 120 146 "structure_factor_mode": 1, # beta approx 147 "radius_effective_mode": 0 # 119 148 }, 0.01, 1316.2990966463444 ], 120 149 [{"@S": "hardsphere", 121 150 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 122 151 "volfraction":0.2, 123 "radius_effective": 50.0, # hard sphere structure factor124 "structure_factor_mode": 1, # 0 = normal decoupling approximation, 1 = beta(Q)approx125 "radius_effective_mode": 0 # this used r_eff =0 or default 50?126 }, 0.01, 1324.7375636587356],152 "radius_effective":120.0, # over ride Reff 153 "structure_factor_mode": 1, # beta approx 154 "radius_effective_mode": 0 # (mode=1 here also uses 120) 155 }, [0.01,0.1,0.2], [1.57928589e+03, 7.37067923e-01, 4.67686197e-02 ]], 127 156 [{"@S": "hardsphere", 128 157 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 129 158 "volfraction":0.2, 130 "radius_effective":50.0, # hard sphere structure factor 131 "structure_factor_mode": 1, # 0 = normal decoupling approximation, 1 = beta(Q) approx 132 "radius_effective_mode": 1 # this used 120 ??? 133 }, 0.01, 1579.2858949296565 ] 159 #"radius_effective":120.0, # hard sphere structure factor 160 "structure_factor_mode": 0, # normal decoupling approximation 161 "radius_effective_mode": 1 # this uses 120 from the form factor 162 }, [0.01,0.1,0.2], [1.10112335e+03, 7.41366536e-01, 4.66630207e-02]], 163 [{"@S": "hardsphere", 164 "radius": 120., "radius_pd": 0.2, "radius_pd_n":45, 165 "volfraction":0.2, 166 #"radius_effective":50.0, # hard sphere structure factor 167 "structure_factor_mode": 0, # normal decoupling approximation 168 "radius_effective_mode": 0 # this used 50 the default for hardsphere 169 }, [0.01,0.1,0.2], [7.82803598e+02, 6.85943611e-01, 4.71586457e-02 ]] 134 170 ] 135 # putting None for expected result will pass the test if there are no errors from the routine, but without any check on the value of the result171 # -
sasmodels/models/squarewell.py
rdb1d9d5 r4d00de6 20 20 Earlier versions of SasView did not incorporate the so-called 21 21 $\beta(q)$ ("beta") correction [2] for polydispersity and non-sphericity. 22 This is only available in SasView versions 4.2.2and higher.22 This is only available in SasView versions 5.0 and higher. 23 23 24 24 The well width $(\lambda)$ is defined as multiples of the particle diameter -
sasmodels/models/stickyhardsphere.py
rdb1d9d5 r4d00de6 56 56 Earlier versions of SasView did not incorporate the so-called 57 57 $\beta(q)$ ("beta") correction [3] for polydispersity and non-sphericity. 58 This is only available in SasView versions 4.2.2and higher.58 This is only available in SasView versions 5.0 and higher. 59 59 60 60 In SasView the effective radius may be calculated from the parameters -
sasmodels/sasview_model.py
ra34b811 rd0b0f5d 31 31 from . import modelinfo 32 32 from .details import make_kernel_args, dispersion_mesh 33 34 # Hack: load in any custom distributions 35 # Uses ~/.sasview/weights/*.py unless SASMODELS_WEIGHTS is set in the environ. 36 # Override with weights.load_weights(pattern="<weights_path>/*.py") 37 weights.load_weights() 33 38 34 39 # pylint: disable=unused-import -
sasmodels/sesans.py
rb297ba9 rda33725 15 15 from numpy import pi # type: ignore 16 16 from scipy.special import j0 17 17 18 18 19 class SesansTransform(object): … … 38 39 39 40 # transform arrays 40 _H = None # type: np.ndarray41 _H0 = None # type: np.ndarray41 _H = None # type: np.ndarray 42 _H0 = None # type: np.ndarray 42 43 43 def __init__(self, z, SElength, lam, zaccept, Rmax ):44 def __init__(self, z, SElength, lam, zaccept, Rmax, log_spacing=1.0003): 44 45 # type: (np.ndarray, float, float) -> None 45 #import logging; logging.info("creating SESANS transform")46 46 self.q = z 47 self.log_spacing = log_spacing 47 48 self._set_hankel(SElength, lam, zaccept, Rmax) 48 49 … … 59 60 def _set_hankel(self, SElength, lam, zaccept, Rmax): 60 61 # type: (np.ndarray, float, float) -> None 61 # Force float32 arrays, otherwise run into memory problems on some machines 62 SElength = np.asarray(SElength, dtype='float32') 63 64 #Rmax = #value in text box somewhere in FitPage? 62 SElength = np.asarray(SElength) 65 63 q_max = 2*pi / (SElength[1] - SElength[0]) 66 64 q_min = 0.1 * 2*pi / (np.size(SElength) * SElength[-1]) 67 q = np. arange(q_min, q_max, q_min, dtype='float32')68 dq = q_min65 q = np.exp(np.arange(np.log(q_min), np.log(q_max), 66 np.log(self.log_spacing))) 69 67 70 H0 = np.float32(dq/(2*pi)) * q 68 dq = np.diff(q) 69 dq = np.insert(dq, 0, dq[0]) 71 70 72 repq = np.tile(q, (SElength.size, 1)).T 73 repSE = np.tile(SElength, (q.size, 1)) 74 H = np.float32(dq/(2*pi)) * j0(repSE*repq) * repq 71 H0 = dq/(2*pi) * q 75 72 76 replam = np.tile(lam, (q.size, 1)) 77 reptheta = np.arcsin(repq*replam/2*np.pi) 73 H = np.outer(q, SElength) 74 j0(H, out=H) 75 H *= (dq * q / (2*pi)).reshape((-1, 1)) 76 77 reptheta = np.outer(q, lam/(2*pi)) 78 np.arcsin(reptheta, out=reptheta) 78 79 mask = reptheta > zaccept 79 80 H[mask] = 0 -
sasmodels/weights.py
rb297ba9 rd0b0f5d 230 230 )) 231 231 232 SAS_WEIGHTS_PATH = "~/.sasview/weights" 233 def load_weights(pattern=None): 234 # type: (str) -> None 235 """ 236 Load dispersion distributions matching the given glob pattern 237 """ 238 import logging 239 import os 240 import os.path 241 import glob 242 import traceback 243 from .custom import load_custom_kernel_module 244 if pattern is None: 245 path = os.environ.get("SAS_WEIGHTS_PATH", SAS_WEIGHTS_PATH) 246 pattern = os.path.join(path, "*.py") 247 for filename in sorted(glob.glob(os.path.expanduser(pattern))): 248 try: 249 #print("loading weights from", filename) 250 module = load_custom_kernel_module(filename) 251 MODELS[module.Dispersion.type] = module.Dispersion 252 except Exception as exc: 253 logging.error(traceback.format_exc(exc)) 232 254 233 255 def get_weights(disperser, n, width, nsigmas, value, limits, relative):
Note: See TracChangeset
for help on using the changeset viewer.