Changes in sasmodels/generate.py [2d81cfe:108e70e] in sasmodels
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/generate.py
r2d81cfe 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 … … 270 279 """ 271 280 281 282 def set_integration_size(info, n): 283 # type: (ModelInfo, int) -> None 284 """ 285 Update the model definition, replacing the gaussian integration with 286 a gaussian integration of a different size. 287 288 Note: this really ought to be a method in modelinfo, but that leads to 289 import loops. 290 """ 291 if (info.source and any(lib.startswith('lib/gauss') for lib in info.source)): 292 import os.path 293 from .gengauss import gengauss 294 path = os.path.join(MODEL_PATH, "lib", "gauss%d.c"%n) 295 if not os.path.exists(path): 296 gengauss(n, path) 297 info.source = ["lib/gauss%d.c"%n if lib.startswith('lib/gauss') 298 else lib for lib in info.source] 272 299 273 300 def format_units(units): … … 608 635 609 636 """ 610 def _gen_fn( name, pars, body, filename, line):611 # type: ( str, List[Parameter], str, str, int) -> str637 def _gen_fn(model_info, name, pars): 638 # type: (ModelInfo, str, List[Parameter]) -> str 612 639 """ 613 640 Generate a function given pars and body. … … 621 648 """ 622 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) 623 654 return _FN_TEMPLATE % { 624 655 'name': name, 'pars': par_decl, 'body': body, 625 'filename': filename.replace('\\', '\\\\'), 'line': line ,656 'filename': filename.replace('\\', '\\\\'), 'line': lineno, 626 657 } 627 658 … … 638 669 639 670 # type in IQXY pattern could be single, float, double, long double, ... 640 _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*[(]", 641 672 flags=re.MULTILINE) 642 def _have_Iqxy(sources):673 def find_xy_mode(source): 643 674 # type: (List[str]) -> bool 644 675 """ 645 Return t rue if any file defines Iqxy.676 Return the xy mode as qa, qac, qabc or qxy. 646 677 647 678 Note this is not a C parser, and so can be easily confused by 648 679 non-standard syntax. Also, it will incorrectly identify the following 649 as having Iqxy::680 as having 2D models:: 650 681 651 682 /* 652 double Iq xy(qx, qy, ...) { ... fill this in later ... }683 double Iqac(qab, qc, ...) { ... fill this in later ... } 653 684 */ 654 685 655 If you want to comment out an Iqxy function, use // on the front of the 656 line instead. 657 """ 658 for _path, code in sources: 659 if _IQXY_PATTERN.search(code): 660 return True 661 return False 662 663 664 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): 665 702 """ 666 703 Add a file to the list of source code chunks, tagged with path and line. 667 704 """ 668 705 path = path.replace('\\', '\\\\') 669 source.append('#line 1 "%s"' % path)706 source.append('#line %d "%s"' % (lineno, path)) 670 707 source.append(code) 671 708 … … 698 735 user_code = [(f, open(f).read()) for f in model_sources(model_info)] 699 736 700 # What kind of 2D model do we need?701 xy_mode = ('qa' if not _have_Iqxy(user_code) and not isinstance(model_info.Iqxy, str)702 else 'qac' if not partable.is_asymmetric703 else 'qabc')704 705 737 # Build initial sources 706 738 source = [] … … 709 741 _add_source(source, code, path) 710 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 711 747 # Make parameters for q, qx, qy so that we can use them in declarations 712 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()] 713 750 # Generate form_volume function, etc. from body only 714 751 if isinstance(model_info.form_volume, str): 715 752 pars = partable.form_volume_parameters 716 source.append(_gen_fn('form_volume', pars, model_info.form_volume, 717 model_info.filename, model_info._form_volume_line)) 753 source.append(_gen_fn(model_info, 'form_volume', pars)) 718 754 if isinstance(model_info.Iq, str): 719 755 pars = [q] + partable.iq_parameters 720 source.append(_gen_fn('Iq', pars, model_info.Iq, 721 model_info.filename, model_info._Iq_line)) 756 source.append(_gen_fn(model_info, 'Iq', pars)) 722 757 if isinstance(model_info.Iqxy, str): 723 pars = [qx, qy] + partable.iqxy_parameters 724 source.append(_gen_fn('Iqxy', pars, model_info.Iqxy, 725 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") 726 780 727 781 # Define the parameter table … … 749 803 if xy_mode == 'qabc': 750 804 pars = ",".join(["_qa", "_qb", "_qc"] + model_refs) 751 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 752 806 clear_iqxy = "#undef CALL_IQ_ABC" 753 807 elif xy_mode == 'qac': 754 808 pars = ",".join(["_qa", "_qc"] + model_refs) 755 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 756 810 clear_iqxy = "#undef CALL_IQ_AC" 757 el se: # xy_mode == 'qa'811 elif xy_mode == 'qa': 758 812 pars = ",".join(["_qa"] + model_refs) 759 813 call_iqxy = "#define CALL_IQ_A(_qa,_v) Iq(%s)" % pars 760 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 761 827 762 828 magpars = [k-2 for k, p in enumerate(partable.call_parameters)
Note: See TracChangeset
for help on using the changeset viewer.