Changeset 108e70e in sasmodels for sasmodels/generate.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/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)
Note: See TracChangeset
for help on using the changeset viewer.