Changeset 108e70e in sasmodels
- Timestamp:
- Dec 14, 2017 3:08:45 PM (7 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- ef85a09
- Parents:
- df69efa
- Files:
-
- 30 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/guide/plugin.rst
rd0dc9a3 r108e70e 292 292 **Note: The order of the parameters in the definition will be the order of the 293 293 parameters in the user interface and the order of the parameters in Iq(), 294 Iqxy() and form_volume(). And** *scale* **and** *background* **parameters are 295 implicit to all models, so they do not need to be included in the parameter table.** 294 Iqac(), Iqabc() and form_volume(). And** *scale* **and** *background* 295 **parameters are implicit to all models, so they do not need to be included 296 in the parameter table.** 296 297 297 298 - **"name"** is the name of the parameter shown on the FitPage. … … 362 363 scattered intensity. 363 364 364 - "volume" parameters are passed to Iq(), Iqxy(), and form_volume(), and 365 have polydispersity loops generated automatically. 366 367 - "orientation" parameters are only passed to Iqxy(), and have angular 368 dispersion. 365 - "volume" parameters are passed to Iq(), Iqac(), Iqabc() and form_volume(), 366 and have polydispersity loops generated automatically. 367 368 - "orientation" parameters are not passed, but instead are combined with 369 orientation dispersity to translate *qx* and *qy* to *qa*, *qb* and *qc*. 370 These parameters should appear at the end of the table with the specific 371 names *theta*, *phi* and for asymmetric shapes *psi*, in that order. 369 372 370 373 Some models will have integer parameters, such as number of pearls in the … … 419 422 That is, the individual models do not need to include polydispersity 420 423 calculations, but instead rely on numerical integration to compute the 421 appropriately smeared pattern. Angular dispersion values over polar angle 422 $\theta$ requires an additional $\cos \theta$ weighting due to decreased 423 arc length for the equatorial angle $\phi$ with increasing latitude. 424 appropriately smeared pattern. 424 425 425 426 Python Models … … 468 469 barbell). If I(q; pars) is NaN for any $q$, then those parameters will be 469 470 ignored, and not included in the calculation of the weighted polydispersity. 470 471 Similar to *Iq*, you can define *Iqxy(qx, qy, par1, par2, ...)* where the472 parameter list includes any orientation parameters. If *Iqxy* is not defined,473 then it will default to *Iqxy = Iq(sqrt(qx**2+qy**2), par1, par2, ...)*.474 471 475 472 Models should define *form_volume(par1, par2, ...)* where the parameter … … 497 494 } 498 495 499 *Iqxy* is similar to *Iq*, except it uses parameters *qx, qy* instead of *q*,500 and it includes orientation parameters.501 502 496 *form_volume* defines the volume of the shape. As in python models, it 503 497 includes only the volume parameters. 504 498 505 *Iqxy* will default to *Iq(sqrt(qx**2 + qy**2), par1, ...)* and506 *form_volume* will default to 1.0.507 508 499 **source=['fn.c', ...]** includes the listed C source files in the 509 program before *Iq* and *Iqxy* are defined. This allows you to extend the 510 library of C functions available to your model. 500 program before *Iq* and *form_volume* are defined. This allows you to 501 extend the library of C functions available to your model. Note that 502 you can put the full function definition for *Iq* and *form_volume* 503 (include function declaration) into an external C file and add it to the list 504 of sources instead of defining it within the python model file. 511 505 512 506 Models are defined using double precision declarations for the … … 532 526 533 527 #define INVALID(v) (v.bell_radius < v.radius) 528 529 Oriented Shapes 530 ............... 531 532 If the scattering is dependent on the orientation of the shape, then you 533 will need to include *orientation* parameters *theta*, *phi* and *psi* 534 at the end of the parameter table. Shape orientation uses *a*, *b* and *c* 535 axes, corresponding to the *x*, *y* and *z* axes in the laboratory coordinate 536 system, with *z* along the beam and *x*-*y* in the detector plane, with *x* 537 horizontal and *y* vertical. The *psi* parameter rotates the shape 538 about its *c* axis, the *theta* parameter then rotates the *c* axis toward 539 the *x* axis of the detector, then *phi* rotates the shape in the detector 540 plane. (Prior to these rotations, orientation dispersity will be applied 541 as roll-pitch-yaw, rotating *c*, then *b* then *a* in the shape coordinate 542 system.) A particular *qx*, *qy* point on the detector, then corresponds 543 to *qa*, *qb*, *qc* with respect to the shape. 544 545 The oriented C model is called as *Iqabc(qa, qb, qc, par1, par2, ...)* where 546 *par1*, etc. are the parameters to the model. If the shape is rotationally 547 symmetric about *c* then *psi* is not needed, and the model is called 548 as *Iqac(qab, qc, par1, par2, ...)*. In either case, the orientation 549 parameters are not included in the function call. 550 551 For 1D oriented shapes, an integral over all angles is usually needed for 552 the *Iq* function. Using symmetry and the substitution $u = \cos(\alpha)$, 553 $du = -\sin(\alpha)\,d\alpha$ this becomes 554 555 .. math:: 556 557 I(q) &= \int_{-\pi/2}^{pi/2} \int_{-pi}^{pi} 558 F(q_a = q \sin(\alpha)\sin(\beta), 559 q_b = q \sin(\alpha)\cos(\beta), 560 q_c = q \cos(\alpha))^2 \sin(\alpha)\,d\beta\,d\alpha/(4\pi) \\ 561 &= 8/(4\pi) \int_{0}^{pi/2} \int_{0}^{\pi/2} F^2 \sin(\alpha)\,d\beta\,d\alpha \\ 562 &= 8/(4\pi) \int_0^1 \int_{0}^{\pi/2} F^2 \,d\beta\,du 563 564 Using the $z, w$ values for Gauss-Legendre integration in "lib/gauss76.c", the 565 numerical integration is then:: 566 567 double outer_sum = 0.0; 568 for (int i = 0; i < GAUSS_N; i++) { 569 const double cos_alpha = 0.5*GAUSS_Z[i] + 0.5; 570 const double sin_alpha = sqrt(1.0 - cos_alpha*cos_alpha); 571 const double qc = cos_alpha * q; 572 double inner_sum = 0.0; 573 for (int j = 0; j < GAUSS_N; j++) { 574 const double beta = M_PI_4 * GAUSS_Z[j] + M_PI_4; 575 double sin_beta, cos_beta; 576 SINCOS(beta, sin_beta, cos_beta); 577 const double qb = sin_alpha * cos_beta * q; 578 const double qa = sin_alpha * sin_beta * q; 579 const double Fq = F(qa, qb, qc, ...); 580 inner_sum += GAUSS_W[j] * Fq*Fq; 581 } 582 outer_sum += GAUSS_W[i] * inner_sum; 583 } 584 outer_sum *= 0.25; // = 8/(4 pi) * outer_sum * (pi/2) / 4 585 586 The *z* values for the Gauss-Legendre integration extends from -1 to 1, so 587 the double sum of *w[i]w[j]* explains the factor of 4. Correcting for the 588 average *dz[i]dz[j]* gives $(1-0) \cdot (\pi/2-0) = \pi/2$. The $8/(4 \pi)$ 589 factor comes from the integral over the quadrant. With less symmetry (eg., 590 in the bcc and fcc paracrystal models), then an integral over the entire 591 sphere may be necessary. 592 593 For simpler models which are rotationally symmetric a single integral 594 suffices: 595 596 .. math:: 597 598 I(q) &= \int_{-\pi/2}^{\pi/2} F(q_{ab} = q \sin(\alpha), 599 q_c = q \cos(\alpha))^2 \sin(\alpha)\,d\alpha/\pi \\ 600 &= (2/\pi) \int_0^1 F^2\,du 601 602 with integration loop:: 603 604 double sum = 0.0; 605 for (int i = 0; i < GAUSS_N; i++) { 606 const double cos_alpha = 0.5*GAUSS_Z[i] + 0.5; 607 const double sin_alpha = sqrt(1.0 - cos_alpha*cos_alpha); 608 const double qc = cos_alpha * q; 609 const double qab = sin_alpha * q; 610 const double Fq = F(qab, qc, ...); 611 sum += GAUSS_W[j] * Fq*Fq; 612 } 613 sum *= 0.5; // = 2/pi * sum * (pi/2) / 2 614 615 Magnetism 616 ......... 617 618 Magnetism is supported automatically for all shapes by modifying the 619 effective SLD of particle according to the Halpern-Johnson vector 620 describing the interation between neutron spin and magnetic field. All 621 parameters marked as type *sld* in the parameter table are treated as 622 possibly magnetic particles with magnitude *M0* and direction 623 *mtheta* and *mphi*. Polarization parameters are also provided 624 automatically for magnetic models to set the spin state of the measurement. 625 626 For more complicated systems where magnetism is not uniform throughout 627 the individual particles, you will need to write your own models. 628 You should not mark the nuclear sld as type *sld*, but instead leave 629 them unmarked and provide your own magnetism and polarization parameters. 630 For 2D measurements you will need $(q_x, q_y)$ values for the measurement 631 to compute the proper magnetism and orientation, which you can implement 632 using *Iqxy(qx, qy, par1, par2, ...)*. 534 633 535 634 Special Functions … … 796 895 show a 50x improvement or more over the equivalent pure python model. 797 896 798 External C Models799 .................800 801 External C models are very much like embedded C models, except that802 *Iq*, *Iqxy* and *form_volume* are defined in an external source file803 loaded using the *source=[...]* statement. You need to supply the function804 declarations for each of these that you need instead of building them805 automatically from the parameter table.806 807 897 808 898 .. _Form_Factors: … … 1006 1096 variable name *Rg* for example because $R_g$ is the right name for the model 1007 1097 parameter then ignore the lint errors. Also, ignore *missing-docstring* 1008 for standard model functions *Iq*, *Iq xy*, etc.1098 for standard model functions *Iq*, *Iqac*, etc. 1009 1099 1010 1100 We will have delinting sessions at the SasView Code Camps, where we can -
sasmodels/details.py
r2d81cfe r108e70e 258 258 # type: (...) -> Sequence[np.ndarray] 259 259 """ 260 **Deprecated** Theta weights will be computed in the kernel wrapper if 261 they are needed. 262 260 263 If there is a theta parameter, update the weights of that parameter so that 261 264 the cosine weighting required for polar integration is preserved. … … 272 275 Returns updated weights vectors 273 276 """ 274 # TODO: explain in a comment why scale and background are missing275 277 # Apparently the parameters.theta_offset similarly skips scale and 276 278 # and background, so the indexing works out, but they are still shipped … … 279 281 index = parameters.theta_offset 280 282 theta = dispersity[index] 281 # TODO: modify the dispersity vector to avoid the theta=-90,90,270,...282 283 theta_weight = abs(cos(radians(theta))) 283 284 weights = tuple(theta_weight*w if k == index else w -
sasmodels/generate.py
ra261a83 r108e70e 7 7 particular dimensions averaged over all orientations. 8 8 9 *Iqxy(qx, qy, p1, p2, ...)* returns the scattering at qx, qy for a form 10 with particular dimensions for a single orientation. 11 12 *Imagnetic(qx, qy, result[], p1, p2, ...)* returns the scattering for the 13 polarized neutron spin states (up-up, up-down, down-up, down-down) for 14 a form with particular dimensions for a single orientation. 9 *Iqac(qab, qc, p1, p2, ...)* returns the scattering at qab, qc 10 for a rotationally symmetric form with particular dimensions. 11 qab, qc are determined from shape orientation and scattering angles. 12 This call is used if the shape has orientation parameters theta and phi. 13 14 *Iqabc(qa, qb, qc, p1, p2, ...)* returns the scattering at qa, qb, qc 15 for a form with particular dimensions. qa, qb, qc are determined from 16 shape orientation and scattering angles. This call is used if the shape 17 has orientation parameters theta, phi and psi. 18 19 *Iqxy(qx, qy, p1, p2, ...)* returns the scattering at qx, qy. Use this 20 to create an arbitrary 2D theory function, needed for q-dependent 21 background functions and for models with non-uniform magnetism. 15 22 16 23 *form_volume(p1, p2, ...)* returns the volume of the form with particular … … 31 38 scale and background parameters for each model. 32 39 33 *Iq*, *Iqxy*, *Imagnetic* and *form_volume* should be stylized C-99 34 functions written for OpenCL. All functions need prototype declarations 35 even if the are defined before they are used. OpenCL does not support 36 *#include* preprocessor directives, so instead the list of includes needs 37 to be given as part of the metadata in the kernel module definition. 38 The included files should be listed using a path relative to the kernel 39 module, or if using "lib/file.c" if it is one of the standard includes 40 provided with the sasmodels source. The includes need to be listed in 41 order so that functions are defined before they are used. 40 C code should be stylized C-99 functions written for OpenCL. All functions 41 need prototype declarations even if the are defined before they are used. 42 Although OpenCL supports *#include* preprocessor directives, the list of 43 includes should be given as part of the metadata in the kernel module 44 definition. The included files should be listed using a path relative to the 45 kernel module, or if using "lib/file.c" if it is one of the standard includes 46 provided with the sasmodels source. The includes need to be listed in order 47 so that functions are defined before they are used. 42 48 43 49 Floating point values should be declared as *double*. For single precision … … 107 113 present, the volume ratio is 1. 108 114 109 *form_volume*, *Iq*, *Iq xy*, *Imagnetic* are strings containing the110 C source code for the body of the volume, Iq, and Iqxyfunctions115 *form_volume*, *Iq*, *Iqac*, *Iqabc* are strings containing 116 the C source code for the body of the volume, Iq, and Iqac functions 111 117 respectively. These can also be defined in the last source file. 112 118 113 *Iq* and *Iqxy* also be instead be python functions defining the119 *Iq*, *Iqac*, *Iqabc* also be instead be python functions defining the 114 120 kernel. If they are marked as *Iq.vectorized = True* then the 115 121 kernel is passed the entire *q* vector at once, otherwise it is … … 168 174 from zlib import crc32 169 175 from inspect import currentframe, getframeinfo 176 import logging 170 177 171 178 import numpy as np # type: ignore … … 181 188 pass 182 189 # pylint: enable=unused-import 190 191 logger = logging.getLogger(__name__) 183 192 184 193 # jitter projection to use in the kernel code. See explore/jitter.py … … 626 635 627 636 """ 628 def _gen_fn( name, pars, body, filename, line):629 # type: ( str, List[Parameter], str, str, int) -> str637 def _gen_fn(model_info, name, pars): 638 # type: (ModelInfo, str, List[Parameter]) -> str 630 639 """ 631 640 Generate a function given pars and body. … … 639 648 """ 640 649 par_decl = ', '.join(p.as_function_argument() for p in pars) if pars else 'void' 650 body = getattr(model_info, name) 651 filename = model_info.filename 652 # Note: if symbol is defined strangely in the module then default it to 1 653 lineno = model_info.lineno.get(name, 1) 641 654 return _FN_TEMPLATE % { 642 655 'name': name, 'pars': par_decl, 'body': body, 643 'filename': filename.replace('\\', '\\\\'), 'line': line ,656 'filename': filename.replace('\\', '\\\\'), 'line': lineno, 644 657 } 645 658 … … 656 669 657 670 # type in IQXY pattern could be single, float, double, long double, ... 658 _IQXY_PATTERN = re.compile( "^((inline|static) )? *([a-z ]+ )? *Iqxy *([(]|$)",671 _IQXY_PATTERN = re.compile(r"(^|\s)double\s+I(?P<mode>q(ab?c|xy))\s*[(]", 659 672 flags=re.MULTILINE) 660 def _have_Iqxy(sources):673 def find_xy_mode(source): 661 674 # type: (List[str]) -> bool 662 675 """ 663 Return t rue if any file defines Iqxy.676 Return the xy mode as qa, qac, qabc or qxy. 664 677 665 678 Note this is not a C parser, and so can be easily confused by 666 679 non-standard syntax. Also, it will incorrectly identify the following 667 as having Iqxy::680 as having 2D models:: 668 681 669 682 /* 670 double Iq xy(qx, qy, ...) { ... fill this in later ... }683 double Iqac(qab, qc, ...) { ... fill this in later ... } 671 684 */ 672 685 673 If you want to comment out an Iqxy function, use // on the front of the 674 line instead. 675 """ 676 for _path, code in sources: 677 if _IQXY_PATTERN.search(code): 678 return True 679 return False 680 681 682 def _add_source(source, code, path): 686 If you want to comment out the function, use // on the front of the 687 line:: 688 689 /* 690 // double Iqac(qab, qc, ...) { ... fill this in later ... } 691 */ 692 693 """ 694 for code in source: 695 m = _IQXY_PATTERN.search(code) 696 if m is not None: 697 return m.group('mode') 698 return 'qa' 699 700 701 def _add_source(source, code, path, lineno=1): 683 702 """ 684 703 Add a file to the list of source code chunks, tagged with path and line. 685 704 """ 686 705 path = path.replace('\\', '\\\\') 687 source.append('#line 1 "%s"' % path)706 source.append('#line %d "%s"' % (lineno, path)) 688 707 source.append(code) 689 708 … … 716 735 user_code = [(f, open(f).read()) for f in model_sources(model_info)] 717 736 718 # What kind of 2D model do we need?719 xy_mode = ('qa' if not _have_Iqxy(user_code) and not isinstance(model_info.Iqxy, str)720 else 'qac' if not partable.is_asymmetric721 else 'qabc')722 723 737 # Build initial sources 724 738 source = [] … … 727 741 _add_source(source, code, path) 728 742 743 if model_info.c_code: 744 _add_source(source, model_info.c_code, model_info.filename, 745 lineno=model_info.lineno.get('c_code', 1)) 746 729 747 # Make parameters for q, qx, qy so that we can use them in declarations 730 q, qx, qy = [Parameter(name=v) for v in ('q', 'qx', 'qy')] 748 q, qx, qy, qab, qa, qb, qc \ 749 = [Parameter(name=v) for v in 'q qx qy qab qa qb qc'.split()] 731 750 # Generate form_volume function, etc. from body only 732 751 if isinstance(model_info.form_volume, str): 733 752 pars = partable.form_volume_parameters 734 source.append(_gen_fn('form_volume', pars, model_info.form_volume, 735 model_info.filename, model_info._form_volume_line)) 753 source.append(_gen_fn(model_info, 'form_volume', pars)) 736 754 if isinstance(model_info.Iq, str): 737 755 pars = [q] + partable.iq_parameters 738 source.append(_gen_fn('Iq', pars, model_info.Iq, 739 model_info.filename, model_info._Iq_line)) 756 source.append(_gen_fn(model_info, 'Iq', pars)) 740 757 if isinstance(model_info.Iqxy, str): 741 pars = [qx, qy] + partable.iqxy_parameters 742 source.append(_gen_fn('Iqxy', pars, model_info.Iqxy, 743 model_info.filename, model_info._Iqxy_line)) 758 pars = [qx, qy] + partable.iq_parameters + partable.orientation_parameters 759 source.append(_gen_fn(model_info, 'Iqxy', pars)) 760 if isinstance(model_info.Iqac, str): 761 pars = [qab, qc] + partable.iq_parameters 762 source.append(_gen_fn(model_info, 'Iqac', pars)) 763 if isinstance(model_info.Iqabc, str): 764 pars = [qa, qb, qc] + partable.iq_parameters 765 source.append(_gen_fn(model_info, 'Iqabc', pars)) 766 767 # What kind of 2D model do we need? Is it consistent with the parameters? 768 xy_mode = find_xy_mode(source) 769 if xy_mode == 'qabc' and not partable.is_asymmetric: 770 raise ValueError("asymmetric oriented models need to define Iqabc") 771 elif xy_mode == 'qac' and partable.is_asymmetric: 772 raise ValueError("symmetric oriented models need to define Iqac") 773 elif not partable.orientation_parameters and xy_mode in ('qac', 'qabc'): 774 raise ValueError("Unexpected function I%s for unoriented shape"%xy_mode) 775 elif partable.orientation_parameters and xy_mode not in ('qac', 'qabc'): 776 if xy_mode == 'qxy': 777 logger.warn("oriented shapes should define Iqac or Iqabc") 778 else: 779 raise ValueError("Expected function Iqac or Iqabc for oriented shape") 744 780 745 781 # Define the parameter table … … 767 803 if xy_mode == 'qabc': 768 804 pars = ",".join(["_qa", "_qb", "_qc"] + model_refs) 769 call_iqxy = "#define CALL_IQ_ABC(_qa,_qb,_qc,_v) Iq xy(%s)" % pars805 call_iqxy = "#define CALL_IQ_ABC(_qa,_qb,_qc,_v) Iqabc(%s)" % pars 770 806 clear_iqxy = "#undef CALL_IQ_ABC" 771 807 elif xy_mode == 'qac': 772 808 pars = ",".join(["_qa", "_qc"] + model_refs) 773 call_iqxy = "#define CALL_IQ_AC(_qa,_qc,_v) Iq xy(%s)" % pars809 call_iqxy = "#define CALL_IQ_AC(_qa,_qc,_v) Iqac(%s)" % pars 774 810 clear_iqxy = "#undef CALL_IQ_AC" 775 el se: # xy_mode == 'qa'811 elif xy_mode == 'qa': 776 812 pars = ",".join(["_qa"] + model_refs) 777 813 call_iqxy = "#define CALL_IQ_A(_qa,_v) Iq(%s)" % pars 778 814 clear_iqxy = "#undef CALL_IQ_A" 815 elif xy_mode == 'qxy': 816 orientation_refs = _call_pars("_v.", partable.orientation_parameters) 817 pars = ",".join(["_qx", "_qy"] + model_refs + orientation_refs) 818 call_iqxy = "#define CALL_IQ_XY(_qx,_qy,_v) Iqxy(%s)" % pars 819 clear_iqxy = "#undef CALL_IQ_XY" 820 if partable.orientation_parameters: 821 call_iqxy += "\n#define HAVE_THETA" 822 clear_iqxy += "\n#undef HAVE_THETA" 823 if partable.is_asymmetric: 824 call_iqxy += "\n#define HAVE_PSI" 825 clear_iqxy += "\n#undef HAVE_PSI" 826 779 827 780 828 magpars = [k-2 for k, p in enumerate(partable.call_parameters) -
sasmodels/kernel_header.c
r8698a0d r108e70e 150 150 inline double cube(double x) { return x*x*x; } 151 151 inline double sas_sinx_x(double x) { return x==0 ? 1.0 : sin(x)/x; } 152 153 // CRUFT: support old style models with orientation received qx, qy and angles 154 155 // To rotate from the canonical position to theta, phi, psi, first rotate by 156 // psi about the major axis, oriented along z, which is a rotation in the 157 // detector plane xy. Next rotate by theta about the y axis, aligning the major 158 // axis in the xz plane. Finally, rotate by phi in the detector plane xy. 159 // To compute the scattering, undo these rotations in reverse order: 160 // rotate in xy by -phi, rotate in xz by -theta, rotate in xy by -psi 161 // The returned q is the length of the q vector and (xhat, yhat, zhat) is a unit 162 // vector in the q direction. 163 // To change between counterclockwise and clockwise rotation, change the 164 // sign of phi and psi. 165 166 #if 1 167 //think cos(theta) should be sin(theta) in new coords, RKH 11Jan2017 168 #define ORIENT_SYMMETRIC(qx, qy, theta, phi, q, sn, cn) do { \ 169 SINCOS(phi*M_PI_180, sn, cn); \ 170 q = sqrt(qx*qx + qy*qy); \ 171 cn = (q==0. ? 1.0 : (cn*qx + sn*qy)/q * sin(theta*M_PI_180)); \ 172 sn = sqrt(1 - cn*cn); \ 173 } while (0) 174 #else 175 // SasView 3.x definition of orientation 176 #define ORIENT_SYMMETRIC(qx, qy, theta, phi, q, sn, cn) do { \ 177 SINCOS(theta*M_PI_180, sn, cn); \ 178 q = sqrt(qx*qx + qy*qy);\ 179 cn = (q==0. ? 1.0 : (cn*cos(phi*M_PI_180)*qx + sn*qy)/q); \ 180 sn = sqrt(1 - cn*cn); \ 181 } while (0) 182 #endif 183 184 #if 1 185 #define ORIENT_ASYMMETRIC(qx, qy, theta, phi, psi, q, xhat, yhat, zhat) do { \ 186 q = sqrt(qx*qx + qy*qy); \ 187 const double qxhat = qx/q; \ 188 const double qyhat = qy/q; \ 189 double sin_theta, cos_theta; \ 190 double sin_phi, cos_phi; \ 191 double sin_psi, cos_psi; \ 192 SINCOS(theta*M_PI_180, sin_theta, cos_theta); \ 193 SINCOS(phi*M_PI_180, sin_phi, cos_phi); \ 194 SINCOS(psi*M_PI_180, sin_psi, cos_psi); \ 195 xhat = qxhat*(-sin_phi*sin_psi + cos_theta*cos_phi*cos_psi) \ 196 + qyhat*( cos_phi*sin_psi + cos_theta*sin_phi*cos_psi); \ 197 yhat = qxhat*(-sin_phi*cos_psi - cos_theta*cos_phi*sin_psi) \ 198 + qyhat*( cos_phi*cos_psi - cos_theta*sin_phi*sin_psi); \ 199 zhat = qxhat*(-sin_theta*cos_phi) \ 200 + qyhat*(-sin_theta*sin_phi); \ 201 } while (0) 202 #else 203 // SasView 3.x definition of orientation 204 #define ORIENT_ASYMMETRIC(qx, qy, theta, phi, psi, q, cos_alpha, cos_mu, cos_nu) do { \ 205 q = sqrt(qx*qx + qy*qy); \ 206 const double qxhat = qx/q; \ 207 const double qyhat = qy/q; \ 208 double sin_theta, cos_theta; \ 209 double sin_phi, cos_phi; \ 210 double sin_psi, cos_psi; \ 211 SINCOS(theta*M_PI_180, sin_theta, cos_theta); \ 212 SINCOS(phi*M_PI_180, sin_phi, cos_phi); \ 213 SINCOS(psi*M_PI_180, sin_psi, cos_psi); \ 214 cos_alpha = cos_theta*cos_phi*qxhat + sin_theta*qyhat; \ 215 cos_mu = (-sin_theta*cos_psi*cos_phi - sin_psi*sin_phi)*qxhat + cos_theta*cos_psi*qyhat; \ 216 cos_nu = (-cos_phi*sin_psi*sin_theta + sin_phi*cos_psi)*qxhat + sin_psi*cos_theta*qyhat; \ 217 } while (0) 218 #endif -
sasmodels/kernel_iq.c
r6aee3ab r108e70e 31 31 // CALL_IQ_AC(qa, qc, table) : call the Iqxy function for symmetric shapes 32 32 // CALL_IQ_ABC(qa, qc, table) : call the Iqxy function for asymmetric shapes 33 // CALL_IQ_XY(qx, qy, table) : call the Iqxy function for arbitrary models 33 34 // INVALID(table) : test if the current point is feesible to calculate. This 34 35 // will be defined in the kernel definition file. … … 469 470 #define APPLY_ROTATION() qabc_apply(rotation, qx, qy, &qa, &qb, &qc) 470 471 #define CALL_KERNEL() CALL_IQ_ABC(qa, qb, qc, local_values.table) 472 #elif defined(CALL_IQ_XY) 473 // direct call to qx,qy calculator 474 double qx, qy; 475 #define FETCH_Q() do { qx = q[2*q_index]; qy = q[2*q_index+1]; } while (0) 476 #define BUILD_ROTATION() do {} while(0) 477 #define APPLY_ROTATION() do {} while(0) 478 #define CALL_KERNEL() CALL_IQ_XY(qx, qy, local_values.table) 471 479 #endif 472 480 … … 477 485 #if defined(CALL_IQ) || defined(CALL_IQ_A) 478 486 #define APPLY_PROJECTION() const double weight=weight0 487 #elif defined(CALL_IQ_XY) 488 // CRUFT: support oriented model which define Iqxy rather than Iqac or Iqabc 489 // Need to plug the values for the orientation angles back into parameter 490 // table in case they were overridden by the orientation offset. This 491 // means that orientation dispersity will not work for these models, but 492 // it was broken anyway, so no matter. Still want to provide Iqxy in case 493 // the user model wants full control of orientation/magnetism. 494 #if defined(HAVE_PSI) 495 const double theta = values[details->theta_par+2]; 496 const double phi = values[details->theta_par+3]; 497 const double psi = values[details->theta_par+4]; 498 double weight; 499 #define APPLY_PROJECTION() do { \ 500 local_values.table.theta = theta; \ 501 local_values.table.phi = phi; \ 502 local_values.table.psi = psi; \ 503 weight=weight0; \ 504 } while (0) 505 #elif defined(HAVE_THETA) 506 const double theta = values[details->theta_par+2]; 507 const double phi = values[details->theta_par+3]; 508 double weight; 509 #define APPLY_PROJECTION() do { \ 510 local_values.table.theta = theta; \ 511 local_values.table.phi = phi; \ 512 weight=weight0; \ 513 } while (0) 514 #else 515 #define APPLY_PROJECTION() const double weight=weight0 516 #endif 479 517 #else // !spherosymmetric projection 480 518 // Grab the "view" angles (theta, phi, psi) from the initial parameter table. -
sasmodels/kernelpy.py
r2d81cfe r108e70e 26 26 # pylint: enable=unused-import 27 27 28 logger = logging.getLogger(__name__) 29 28 30 class PyModel(KernelModel): 29 31 """ … … 31 33 """ 32 34 def __init__(self, model_info): 33 # Make sure Iq and Iqxy areavailable and vectorized35 # Make sure Iq is available and vectorized 34 36 _create_default_functions(model_info) 35 37 self.info = model_info 36 38 self.dtype = np.dtype('d') 37 logg ing.info("load python model " + self.info.name)39 logger.info("load python model " + self.info.name) 38 40 39 41 def make_kernel(self, q_vectors): 40 42 q_input = PyInput(q_vectors, dtype=F64) 41 kernel = self.info.Iqxy if q_input.is_2d else self.info.Iq 42 return PyKernel(kernel, self.info, q_input) 43 return PyKernel(self.info, q_input) 43 44 44 45 def release(self): … … 89 90 Callable SAS kernel. 90 91 91 *kernel* is the DllKernel object to call.92 *kernel* is the kernel object to call. 92 93 93 94 *model_info* is the module information … … 104 105 Call :meth:`release` when done with the kernel instance. 105 106 """ 106 def __init__(self, kernel,model_info, q_input):107 def __init__(self, model_info, q_input): 107 108 # type: (callable, ModelInfo, List[np.ndarray]) -> None 108 109 self.dtype = np.dtype('d') … … 110 111 self.q_input = q_input 111 112 self.res = np.empty(q_input.nq, q_input.dtype) 112 self.kernel = kernel113 113 self.dim = '2d' if q_input.is_2d else '1d' 114 114 … … 159 159 # Generate a closure which calls the form_volume if it exists. 160 160 form_volume = model_info.form_volume 161 self._volume = ((lambda: form_volume(*volume_args)) if form_volume 162 else(lambda: 1.0))161 self._volume = ((lambda: form_volume(*volume_args)) if form_volume else 162 (lambda: 1.0)) 163 163 164 164 def __call__(self, call_details, values, cutoff, magnetic): … … 261 261 any functions that are not already marked as vectorized. 262 262 """ 263 # Note: must call create_vector_Iq before create_vector_Iqxy 263 264 _create_vector_Iq(model_info) 264 _create_vector_Iqxy(model_info) # call create_vector_Iq() first265 _create_vector_Iqxy(model_info) 265 266 266 267 … … 280 281 model_info.Iq = vector_Iq 281 282 283 282 284 def _create_vector_Iqxy(model_info): 283 285 """ 284 286 Define Iqxy as a vector function if it exists, or default it from Iq(). 285 287 """ 286 Iq , Iqxy = model_info.Iq, model_info.Iqxy288 Iqxy = getattr(model_info, 'Iqxy', None) 287 289 if callable(Iqxy): 288 290 if not getattr(Iqxy, 'vectorized', False): … … 295 297 vector_Iqxy.vectorized = True 296 298 model_info.Iqxy = vector_Iqxy 297 el if callable(Iq):299 else: 298 300 #print("defaulting Iqxy") 299 301 # Iq is vectorized because create_vector_Iq was already called. 302 Iq = model_info.Iq 300 303 def default_Iqxy(qx, qy, *args): 301 304 """ -
sasmodels/modelinfo.py
r2d81cfe r108e70e 37 37 38 38 # assumptions about common parameters exist throughout the code, such as: 39 # (1) kernel functions Iq, Iqxy, form_volume, ... don't see them39 # (1) kernel functions Iq, Iqxy, Iqac, Iqabc, form_volume, ... don't see them 40 40 # (2) kernel drivers assume scale is par[0] and background is par[1] 41 41 # (3) mixture models drop the background on components and replace the scale … … 256 256 257 257 *type* indicates how the parameter will be used. "volume" parameters 258 will be used in all functions. "orientation" parameters will be used259 in *Iqxy* and *Imagnetic*. "magnetic* parameters will be used in260 *I magnetic* only. If *type* is the empty string, the parameter will258 will be used in all functions. "orientation" parameters are not passed, 259 but will be used to convert from *qx*, *qy* to *qa*, *qb*, *qc* in calls to 260 *Iqxy* and *Imagnetic*. If *type* is the empty string, the parameter will 261 261 be used in all of *Iq*, *Iqxy* and *Imagnetic*. "sld" parameters 262 262 can automatically be promoted to magnetic parameters, each of which … … 386 386 with vector parameter p sent as p[]. 387 387 388 * [removed] *iqxy_parameters* is the list of parameters to the Iqxy(qx, qy, ...)389 function, with vector parameter p sent as p[].390 391 388 * *form_volume_parameters* is the list of parameters to the form_volume(...) 392 389 function, with vector parameter p sent as p[]. … … 443 440 self.iq_parameters = [p for p in self.kernel_parameters 444 441 if p.type not in ('orientation', 'magnetic')] 445 # note: orientation no longer sent to Iqxy, so its the same as 446 #self.iqxy_parameters = [p for p in self.kernel_parameters 447 # if p.type != 'magnetic'] 442 self.orientation_parameters = [p for p in self.kernel_parameters 443 if p.type == 'orientation'] 448 444 self.form_volume_parameters = [p for p in self.kernel_parameters 449 445 if p.type == 'volume'] … … 490 486 if p.type != 'orientation': 491 487 raise TypeError("psi must be an orientation parameter") 488 elif p.type == 'orientation': 489 raise TypeError("only theta, phi and psi can be orientation parameters") 492 490 if theta >= 0 and phi >= 0: 491 last_par = len(self.kernel_parameters) - 1 493 492 if phi != theta+1: 494 493 raise TypeError("phi must follow theta") 495 494 if psi >= 0 and psi != phi+1: 496 495 raise TypeError("psi must follow phi") 496 #if (psi >= 0 and psi != last_par) or (psi < 0 and phi != last_par): 497 # raise TypeError("orientation parameters must appear at the " 498 # "end of the parameter table") 497 499 elif theta >= 0 or phi >= 0 or psi >= 0: 498 500 raise TypeError("oriented shapes must have both theta and phi and maybe psi") … … 715 717 716 718 719 #: Set of variables defined in the model that might contain C code 720 C_SYMBOLS = ['Imagnetic', 'Iq', 'Iqxy', 'Iqac', 'Iqabc', 'form_volume', 'c_code'] 721 717 722 def _find_source_lines(model_info, kernel_module): 718 723 # type: (ModelInfo, ModuleType) -> None … … 720 725 Identify the location of the C source inside the model definition file. 721 726 722 This code runs through the source of the kernel module looking for 723 lines that start with 'Iq', 'Iqxy' or 'form_volume'. Clearly there are 724 all sorts of reasons why this might not work (e.g., code commented out 725 in a triple-quoted line block, code built using string concatenation, 726 or code defined in the branch of an 'if' block), but it should work 727 properly in the 95% case, and getting the incorrect line number will 728 be harmless. 729 """ 730 # Check if we need line numbers at all 731 if callable(model_info.Iq): 732 return None 733 734 if (model_info.Iq is None 735 and model_info.Iqxy is None 736 and model_info.Imagnetic is None 737 and model_info.form_volume is None): 727 This code runs through the source of the kernel module looking for lines 728 that contain C code (because it is a c function definition). Clearly 729 there are all sorts of reasons why this might not work (e.g., code 730 commented out in a triple-quoted line block, code built using string 731 concatenation, code defined in the branch of an 'if' block, code imported 732 from another file), but it should work properly in the 95% case, and for 733 the remainder, getting the incorrect line number will merely be 734 inconvenient. 735 """ 736 # Only need line numbers if we are creating a C module and the C symbols 737 # are defined. 738 if (callable(model_info.Iq) 739 or not any(hasattr(model_info, s) for s in C_SYMBOLS)): 738 740 return 739 741 740 # find the defintion lines for the different code blocks742 # load the module source if we can 741 743 try: 742 744 source = inspect.getsource(kernel_module) 743 745 except IOError: 744 746 return 745 for k, v in enumerate(source.split('\n')): 746 if v.startswith('Imagnetic'): 747 model_info._Imagnetic_line = k+1 748 elif v.startswith('Iqxy'): 749 model_info._Iqxy_line = k+1 750 elif v.startswith('Iq'): 751 model_info._Iq_line = k+1 752 elif v.startswith('form_volume'): 753 model_info._form_volume_line = k+1 754 747 748 # look for symbol at the start of the line 749 for lineno, line in enumerate(source.split('\n')): 750 for name in C_SYMBOLS: 751 if line.startswith(name): 752 # Add 1 since some compilers complain about "#line 0" 753 model_info.lineno[name] = lineno + 1 754 break 755 755 756 756 def make_model_info(kernel_module): … … 761 761 Fill in default values for parts of the module that are not provided. 762 762 763 Note: vectorized Iq and Iq xyfunctions will be created for python763 Note: vectorized Iq and Iqac/Iqabc functions will be created for python 764 764 models when the model is first called, not when the model is loaded. 765 765 """ … … 790 790 info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y']) 791 791 info.source = getattr(kernel_module, 'source', []) 792 info.c_code = getattr(kernel_module, 'c_code', None) 792 793 # TODO: check the structure of the tests 793 794 info.tests = getattr(kernel_module, 'tests', []) … … 797 798 info.Iq = getattr(kernel_module, 'Iq', None) # type: ignore 798 799 info.Iqxy = getattr(kernel_module, 'Iqxy', None) # type: ignore 800 info.Iqac = getattr(kernel_module, 'Iqac', None) # type: ignore 801 info.Iqabc = getattr(kernel_module, 'Iqabc', None) # type: ignore 799 802 info.Imagnetic = getattr(kernel_module, 'Imagnetic', None) # type: ignore 800 803 info.profile = getattr(kernel_module, 'profile', None) # type: ignore … … 811 814 info.hidden = getattr(kernel_module, 'hidden', None) # type: ignore 812 815 816 if callable(info.Iq) and parameters.has_2d: 817 raise ValueError("oriented python models not supported") 818 819 info.lineno = {} 813 820 _find_source_lines(info, kernel_module) 814 821 … … 824 831 825 832 The structure should be mostly static, other than the delayed definition 826 of *Iq* and *Iqxy* if they need to be defined.833 of *Iq*, *Iqac* and *Iqabc* if they need to be defined. 827 834 """ 828 835 #: Full path to the file defining the kernel, if any. … … 906 913 structure_factor = None # type: bool 907 914 #: List of C source files used to define the model. The source files 908 #: should define the *Iq* function, and possibly *Iq xy*, though a default909 #: *Iqxy = Iq(sqrt(qx**2+qy**2)* will be created if no *Iqxy* is provided.910 #: Files containing the most basic functions must appear first in the list,911 #: followed by the files that use those functions. Form factors are912 #: indicated by providing a:attr:`ER` function.915 #: should define the *Iq* function, and possibly *Iqac* or *Iqabc* if the 916 #: model defines orientation parameters. Files containing the most basic 917 #: functions must appear first in the list, followed by the files that 918 #: use those functions. Form factors are indicated by providing 919 #: an :attr:`ER` function. 913 920 source = None # type: List[str] 914 921 #: The set of tests that must pass. The format of the tests is described … … 935 942 #: See :attr:`ER` for details on the parameters. 936 943 VR = None # type: Optional[Callable[[np.ndarray], Tuple[np.ndarray, np.ndarray]]] 944 #: Arbitrary C code containing supporting functions, etc., to be inserted 945 #: after everything in source. This can include Iq and Iqxy functions with 946 #: the full function signature, including all parameters. 947 c_code = None 937 948 #: Returns the form volume for python-based models. Form volume is needed 938 949 #: for volume normalization in the polydispersity integral. If no … … 955 966 #: include the decimal point. See :mod:`generate` for more details. 956 967 Iq = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 957 #: Returns *I(qx, qy, a, b, ...)*. The interface follows :attr:`Iq`. 958 Iqxy = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 968 #: Returns *I(qab, qc, a, b, ...)*. The interface follows :attr:`Iq`. 969 Iqac = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 970 #: Returns *I(qa, qb, qc, a, b, ...)*. The interface follows :attr:`Iq`. 971 Iqabc = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 959 972 #: Returns *I(qx, qy, a, b, ...)*. The interface follows :attr:`Iq`. 960 973 Imagnetic = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] … … 972 985 #: Returns a random parameter set for the model 973 986 random = None # type: Optional[Callable[[], Dict[str, float]]] 974 975 # line numbers within the python file for bits of C source, if defined 976 # NB: some compilers fail with a "#line 0" directive, so default to 1. 977 _Imagnetic_line = 1 978 _Iqxy_line = 1 979 _Iq_line = 1 980 _form_volume_line = 1 981 987 #: Line numbers for symbols defining C code 988 lineno = None # type: Dict[str, int] 982 989 983 990 def __init__(self): -
sasmodels/models/_spherepy.py
ref07e95 r108e70e 88 88 Iq.vectorized = True # Iq accepts an array of q values 89 89 90 def Iqxy(qx, qy, sld, sld_solvent, radius):91 return Iq(sqrt(qx ** 2 + qy ** 2), sld, sld_solvent, radius)92 Iqxy.vectorized = True # Iqxy accepts arrays of qx, qy values93 94 90 def sesans(z, sld, sld_solvent, radius): 95 91 """ -
sasmodels/models/barbell.c
r74768cb r108e70e 90 90 91 91 static double 92 Iq xy(double qab, double qc,92 Iqac(double qab, double qc, 93 93 double sld, double solvent_sld, 94 94 double radius_bell, double radius, double length) -
sasmodels/models/bcc_paracrystal.c
r74768cb r108e70e 107 107 108 108 109 static double Iq xy(double qa, double qb, double qc,109 static double Iqabc(double qa, double qb, double qc, 110 110 double dnn, double d_factor, double radius, 111 111 double sld, double solvent_sld) -
sasmodels/models/capped_cylinder.c
r74768cb r108e70e 115 115 116 116 static double 117 Iq xy(double qab, double qc,117 Iqac(double qab, double qc, 118 118 double sld, double solvent_sld, double radius, 119 119 double radius_cap, double length) -
sasmodels/models/core_shell_bicelle.c
r74768cb r108e70e 67 67 68 68 static double 69 Iq xy(double qab, double qc,69 Iqac(double qab, double qc, 70 70 double radius, 71 71 double thick_rim, -
sasmodels/models/core_shell_bicelle_elliptical.c
rd4db147 r108e70e 71 71 72 72 static double 73 Iq xy(double qa, double qb, double qc,73 Iqabc(double qa, double qb, double qc, 74 74 double r_minor, 75 75 double x_core, -
sasmodels/models/core_shell_bicelle_elliptical_belt_rough.c
rd4db147 r108e70e 79 79 80 80 static double 81 Iq xy(double qa, double qb, double qc,81 Iqabc(double qa, double qb, double qc, 82 82 double r_minor, 83 83 double x_core, … … 114 114 return 1.0e-4 * Aq*exp(-0.5*(square(qa) + square(qb) + square(qc) )*square(sigma)); 115 115 } 116 -
sasmodels/models/core_shell_bicelle_elliptical_belt_rough.py
r110f69c r108e70e 149 149 ["sld_rim", "1e-6/Ang^2", 1, [-inf, inf], "sld", "Cylinder rim scattering length density"], 150 150 ["sld_solvent", "1e-6/Ang^2", 6, [-inf, inf], "sld", "Solvent scattering length density"], 151 ["sigma", "Ang", 0, [0, inf], "", "interfacial roughness"], 151 152 ["theta", "degrees", 90.0, [-360, 360], "orientation", "cylinder axis to beam angle"], 152 153 ["phi", "degrees", 0, [-360, 360], "orientation", "rotation about beam"], 153 154 ["psi", "degrees", 0, [-360, 360], "orientation", "rotation about cylinder axis"], 154 ["sigma", "Ang", 0, [0, inf], "", "interfacial roughness"]155 155 ] 156 156 -
sasmodels/models/core_shell_cylinder.c
r74768cb r108e70e 48 48 49 49 50 double Iq xy(double qab, double qc,50 double Iqac(double qab, double qc, 51 51 double core_sld, 52 52 double shell_sld, -
sasmodels/models/core_shell_ellipsoid.c
r74768cb r108e70e 75 75 76 76 static double 77 Iq xy(double qab, double qc,77 Iqac(double qab, double qc, 78 78 double radius_equat_core, 79 79 double x_core, -
sasmodels/models/core_shell_parallelepiped.c
ra261a83 r108e70e 102 102 103 103 static double 104 Iq xy(double qa, double qb, double qc,104 Iqabc(double qa, double qb, double qc, 105 105 double core_sld, 106 106 double arim_sld, -
sasmodels/models/cylinder.c
r74768cb r108e70e 45 45 46 46 static double 47 Iq xy(double qab, double qc,47 Iqac(double qab, double qc, 48 48 double sld, 49 49 double solvent_sld, -
sasmodels/models/ellipsoid.c
r74768cb r108e70e 39 39 40 40 static double 41 Iq xy(double qab, double qc,41 Iqac(double qab, double qc, 42 42 double sld, 43 43 double sld_solvent, -
sasmodels/models/elliptical_cylinder.c
rd4db147 r108e70e 54 54 55 55 static double 56 Iq xy(double qa, double qb, double qc,56 Iqabc(double qa, double qb, double qc, 57 57 double radius_minor, double r_ratio, double length, 58 58 double sld, double solvent_sld) -
sasmodels/models/fcc_paracrystal.c
r74768cb r108e70e 80 80 81 81 82 static double Iq xy(double qa, double qb, double qc,82 static double Iqabc(double qa, double qb, double qc, 83 83 double dnn, double d_factor, double radius, 84 84 double sld, double solvent_sld) -
sasmodels/models/hollow_cylinder.c
r74768cb r108e70e 52 52 53 53 static double 54 Iq xy(double qab, double qc,54 Iqac(double qab, double qc, 55 55 double radius, double thickness, double length, 56 56 double sld, double solvent_sld) -
sasmodels/models/hollow_rectangular_prism.c
r74768cb r108e70e 85 85 } 86 86 87 double Iq xy(double qa, double qb, double qc,87 double Iqabc(double qa, double qb, double qc, 88 88 double sld, 89 89 double solvent_sld, -
sasmodels/models/line.py
r2d81cfe r108e70e 57 57 Iq.vectorized = True # Iq accepts an array of q values 58 58 59 59 60 def Iqxy(qx, qy, *args): 60 61 """ … … 69 70 70 71 Iqxy.vectorized = True # Iqxy accepts an array of qx qy values 72 73 # uncomment the following to test Iqxy in C models 74 #del Iq, Iqxy 75 #c_code = """ 76 #static double Iq(double q, double b, double m) { return m*q+b; } 77 #static double Iqxy(double qx, double qy, double b, double m) 78 #{ return (m*qx+b)*(m*qy+b); } 79 #""" 71 80 72 81 def random(): -
sasmodels/models/parallelepiped.c
r74768cb r108e70e 53 53 54 54 static double 55 Iq xy(double qa, double qb, double qc,55 Iqabc(double qa, double qb, double qc, 56 56 double sld, 57 57 double solvent_sld, -
sasmodels/models/rectangular_prism.c
r74768cb r108e70e 71 71 72 72 73 double Iq xy(double qa, double qb, double qc,73 double Iqabc(double qa, double qb, double qc, 74 74 double sld, 75 75 double solvent_sld, -
sasmodels/models/sc_paracrystal.c
r74768cb r108e70e 82 82 83 83 static double 84 Iq xy(double qa, double qb, double qc,84 Iqabc(double qa, double qb, double qc, 85 85 double dnn, double d_factor, double radius, 86 86 double sld, double solvent_sld) -
sasmodels/models/stacked_disks.c
r74768cb r108e70e 142 142 143 143 static double 144 Iq xy(double qab, double qc,144 Iqac(double qab, double qc, 145 145 double thick_core, 146 146 double thick_layer, -
sasmodels/models/triaxial_ellipsoid.c
r74768cb r108e70e 46 46 47 47 static double 48 Iq xy(double qa, double qb, double qc,48 Iqabc(double qa, double qb, double qc, 49 49 double sld, 50 50 double sld_solvent,
Note: See TracChangeset
for help on using the changeset viewer.