Changeset c1799d3 in sasmodels for sasmodels/generate.py
- Timestamp:
- Nov 9, 2018 2:16:05 PM (5 years ago)
- Branches:
- master, core_shell_microgels, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- c11d09f
- Parents:
- 17695aa (diff), cf3d0ce (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. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/generate.py
rb6dab59 rc1799d3 24 24 dimension, or 1.0 if no volume normalization is required. 25 25 26 *ER(p1, p2, ...)* returns the effective radius of the form with 27 particular dimensions. 28 29 *VR(p1, p2, ...)* returns the volume ratio for core-shell style forms. 26 *shell_volume(p1, p2, ...)* returns the volume of the shell for forms 27 which are hollow. 28 29 *effective_radius(mode, p1, p2, ...)* returns the effective radius of 30 the form with particular dimensions. Mode determines the type of 31 effective radius returned, with mode=1 for equivalent volume. 30 32 31 33 #define INVALID(v) (expr) returns False if v.parameter is invalid … … 72 74 background. This will work correctly even when polydispersity is off. 73 75 74 *ER* and *VR* are python functions which operate on parameter vectors.75 The constructor code will generate the necessary vectors for computing76 them with the desired polydispersity.77 76 The kernel module must set variables defining the kernel meta data: 78 77 … … 106 105 create the OpenCL kernel functions. The files defining the functions 107 106 need to be listed before the files which use the functions. 108 109 *ER* is a python function defining the effective radius. If it is110 not present, the effective radius is 0.111 112 *VR* is a python function defining the volume ratio. If it is not113 present, the volume ratio is 1.114 107 115 108 *form_volume*, *Iq*, *Iqac*, *Iqabc* are strings containing … … 671 664 672 665 673 # type in IQXY pattern could be single, float, double, long double, ... 674 _IQXY_PATTERN = re.compile(r"(^|\s)double\s+I(?P<mode>q(ab?c|xy))\s*[(]", 666 _IQXY_PATTERN = re.compile(r"(^|\s)double\s+I(?P<mode>q(ac|abc|xy))\s*[(]", 675 667 flags=re.MULTILINE) 676 668 def find_xy_mode(source): … … 701 693 return 'qa' 702 694 695 # Note: not presently used. Need to know whether Fq is available before 696 # trying to compile the source. Leave the code here in case we decide that 697 # define have_Fq for each form factor is too tedious and error prone. 698 _FQ_PATTERN = re.compile(r"(^|\s)void\s+Fq[(]", flags=re.MULTILINE) 699 def contains_Fq(source): 700 # type: (List[str]) -> bool 701 """ 702 Return True if C source defines "void Fq(". 703 """ 704 for code in source: 705 m = _FQ_PATTERN.search(code) 706 if m is not None: 707 return True 708 return False 709 710 _SHELL_VOLUME_PATTERN = re.compile(r"(^|\s)double\s+shell_volume[(]", flags=re.MULTILINE) 711 def contains_shell_volume(source): 712 # type: (List[str]) -> bool 713 """ 714 Return True if C source defines "void Fq(". 715 """ 716 for code in source: 717 m = _SHELL_VOLUME_PATTERN.search(code) 718 if m is not None: 719 return True 720 return False 703 721 704 722 def _add_source(source, code, path, lineno=1): … … 730 748 # dispersion. Need to be careful that necessary parameters are available 731 749 # for computing volume even if we allow non-disperse volume parameters. 732 733 750 partable = model_info.parameters 734 751 … … 743 760 for path, code in user_code: 744 761 _add_source(source, code, path) 745 746 762 if model_info.c_code: 747 763 _add_source(source, model_info.c_code, model_info.filename, … … 751 767 q, qx, qy, qab, qa, qb, qc \ 752 768 = [Parameter(name=v) for v in 'q qx qy qab qa qb qc'.split()] 769 753 770 # Generate form_volume function, etc. from body only 754 771 if isinstance(model_info.form_volume, str): 755 772 pars = partable.form_volume_parameters 756 773 source.append(_gen_fn(model_info, 'form_volume', pars)) 774 if isinstance(model_info.shell_volume, str): 775 pars = partable.form_volume_parameters 776 source.append(_gen_fn(model_info, 'shell_volume', pars)) 757 777 if isinstance(model_info.Iq, str): 758 778 pars = [q] + partable.iq_parameters … … 767 787 pars = [qa, qb, qc] + partable.iq_parameters 768 788 source.append(_gen_fn(model_info, 'Iqabc', pars)) 789 790 # Check for shell_volume in source 791 is_hollow = contains_shell_volume(source) 769 792 770 793 # What kind of 2D model do we need? Is it consistent with the parameters? … … 789 812 source.append("\\\n".join(p.as_definition() 790 813 for p in partable.kernel_parameters)) 791 792 814 # Define the function calls 815 call_effective_radius = "#define CALL_EFFECTIVE_RADIUS(_mode, _v) 0.0" 793 816 if partable.form_volume_parameters: 794 817 refs = _call_pars("_v.", partable.form_volume_parameters) 795 call_volume = "#define CALL_VOLUME(_v) form_volume(%s)"%(",".join(refs)) 818 if is_hollow: 819 call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = form_volume(%s); _shell = shell_volume(%s); } while (0)"%((",".join(refs),)*2) 820 else: 821 call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = _shell = form_volume(%s); } while (0)"%(",".join(refs)) 822 if model_info.effective_radius_type: 823 call_effective_radius = "#define CALL_EFFECTIVE_RADIUS(_mode, _v) effective_radius(_mode, %s)"%(",".join(refs)) 796 824 else: 797 825 # Model doesn't have volume. We could make the kernel run a little 798 826 # faster by not using/transferring the volume normalizations, but 799 827 # the ifdef's reduce readability more than is worthwhile. 800 call_volume = "#define CALL_VOLUME( v) 1.0"828 call_volume = "#define CALL_VOLUME(_form, _shell, _v) do { _form = _shell = 1.0; } while (0)" 801 829 source.append(call_volume) 802 830 source.append(call_effective_radius) 803 831 model_refs = _call_pars("_v.", partable.iq_parameters) 804 pars = ",".join(["_q"] + model_refs) 805 call_iq = "#define CALL_IQ(_q, _v) Iq(%s)" % pars 832 833 if model_info.have_Fq: 834 pars = ",".join(["_q", "&_F1", "&_F2",] + model_refs) 835 call_iq = "#define CALL_FQ(_q, _F1, _F2, _v) Fq(%s)" % pars 836 clear_iq = "#undef CALL_FQ" 837 else: 838 pars = ",".join(["_q"] + model_refs) 839 call_iq = "#define CALL_IQ(_q, _v) Iq(%s)" % pars 840 clear_iq = "#undef CALL_IQ" 806 841 if xy_mode == 'qabc': 807 842 pars = ",".join(["_qa", "_qb", "_qc"] + model_refs) … … 812 847 call_iqxy = "#define CALL_IQ_AC(_qa,_qc,_v) Iqac(%s)" % pars 813 848 clear_iqxy = "#undef CALL_IQ_AC" 814 elif xy_mode == 'qa' :849 elif xy_mode == 'qa' and not model_info.have_Fq: 815 850 pars = ",".join(["_qa"] + model_refs) 816 851 call_iqxy = "#define CALL_IQ_A(_qa,_v) Iq(%s)" % pars 817 852 clear_iqxy = "#undef CALL_IQ_A" 853 elif xy_mode == 'qa' and model_info.have_Fq: 854 pars = ",".join(["_qa", "&_F1", "&_F2",] + model_refs) 855 # Note: uses rare C construction (expr1, expr2) which computes 856 # expr1 then expr2 and evaluates to expr2. This allows us to 857 # leave it looking like a function even though it is returning 858 # its values by reference. 859 call_iqxy = "#define CALL_FQ_A(_qa,_F1,_F2,_v) (Fq(%s),_F2)" % pars 860 clear_iqxy = "#undef CALL_FQ_A" 818 861 elif xy_mode == 'qxy': 819 862 orientation_refs = _call_pars("_v.", partable.orientation_parameters) … … 831 874 magpars = [k-2 for k, p in enumerate(partable.call_parameters) 832 875 if p.type == 'sld'] 833 834 876 # Fill in definitions for numbers of parameters 835 877 source.append("#define MAX_PD %s"%partable.max_pd) … … 839 881 source.append("#define MAGNETIC_PARS %s"%",".join(str(k) for k in magpars)) 840 882 source.append("#define PROJECTION %d"%PROJECTION) 841 842 883 # TODO: allow mixed python/opencl kernels? 843 844 ocl = _kernels(kernel_code, call_iq, call_iqxy, clear_iqxy, model_info.name)845 dll = _kernels(kernel_code, call_iq, call_iqxy, clear_iqxy, model_info.name) 884 ocl = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name) 885 dll = _kernels(kernel_code, call_iq, clear_iq, call_iqxy, clear_iqxy, model_info.name) 886 846 887 result = { 847 888 'dll': '\n'.join(source+dll[0]+dll[1]+dll[2]), 848 889 'opencl': '\n'.join(source+ocl[0]+ocl[1]+ocl[2]), 849 890 } 850 851 891 return result 852 892 853 893 854 def _kernels(kernel, call_iq, c all_iqxy, clear_iqxy, name):894 def _kernels(kernel, call_iq, clear_iq, call_iqxy, clear_iqxy, name): 855 895 # type: ([str,str], str, str, str) -> List[str] 856 896 code = kernel[0] … … 862 902 '#line 1 "%s Iq"' % path, 863 903 code, 864 "#undef CALL_IQ",904 clear_iq, 865 905 "#undef KERNEL_NAME", 866 906 ]
Note: See TracChangeset
for help on using the changeset viewer.