Changeset 108e70e in sasmodels for sasmodels/modelinfo.py
- Timestamp:
- Dec 14, 2017 1:08:45 PM (6 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- ef85a09
- Parents:
- df69efa
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
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):
Note: See TracChangeset
for help on using the changeset viewer.