Changeset c1799d3 in sasmodels for sasmodels/generate.py


Ignore:
Timestamp:
Nov 9, 2018 2:16:05 PM (5 years ago)
Author:
Paul Kienzle <pkienzle@…>
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.
Message:

Merge branch 'beta_approx' into ticket-1157

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sasmodels/generate.py

    rb6dab59 rc1799d3  
    2424    dimension, or 1.0 if no volume normalization is required. 
    2525 
    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. 
    3032 
    3133    #define INVALID(v) (expr)  returns False if v.parameter is invalid 
     
    7274background.  This will work correctly even when polydispersity is off. 
    7375 
    74 *ER* and *VR* are python functions which operate on parameter vectors. 
    75 The constructor code will generate the necessary vectors for computing 
    76 them with the desired polydispersity. 
    7776The kernel module must set variables defining the kernel meta data: 
    7877 
     
    106105    create the OpenCL kernel functions.  The files defining the functions 
    107106    need to be listed before the files which use the functions. 
    108  
    109     *ER* is a python function defining the effective radius.  If it is 
    110     not present, the effective radius is 0. 
    111  
    112     *VR* is a python function defining the volume ratio.  If it is not 
    113     present, the volume ratio is 1. 
    114107 
    115108    *form_volume*, *Iq*, *Iqac*, *Iqabc* are strings containing 
     
    671664 
    672665 
    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*[(]", 
    675667                           flags=re.MULTILINE) 
    676668def find_xy_mode(source): 
     
    701693    return 'qa' 
    702694 
     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) 
     699def 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) 
     711def 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 
    703721 
    704722def _add_source(source, code, path, lineno=1): 
     
    730748    # dispersion.  Need to be careful that necessary parameters are available 
    731749    # for computing volume even if we allow non-disperse volume parameters. 
    732  
    733750    partable = model_info.parameters 
    734751 
     
    743760    for path, code in user_code: 
    744761        _add_source(source, code, path) 
    745  
    746762    if model_info.c_code: 
    747763        _add_source(source, model_info.c_code, model_info.filename, 
     
    751767    q, qx, qy, qab, qa, qb, qc \ 
    752768        = [Parameter(name=v) for v in 'q qx qy qab qa qb qc'.split()] 
     769 
    753770    # Generate form_volume function, etc. from body only 
    754771    if isinstance(model_info.form_volume, str): 
    755772        pars = partable.form_volume_parameters 
    756773        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)) 
    757777    if isinstance(model_info.Iq, str): 
    758778        pars = [q] + partable.iq_parameters 
     
    767787        pars = [qa, qb, qc] + partable.iq_parameters 
    768788        source.append(_gen_fn(model_info, 'Iqabc', pars)) 
     789 
     790    # Check for shell_volume in source 
     791    is_hollow = contains_shell_volume(source) 
    769792 
    770793    # What kind of 2D model do we need?  Is it consistent with the parameters? 
     
    789812    source.append("\\\n".join(p.as_definition() 
    790813                              for p in partable.kernel_parameters)) 
    791  
    792814    # Define the function calls 
     815    call_effective_radius = "#define CALL_EFFECTIVE_RADIUS(_mode, _v) 0.0" 
    793816    if partable.form_volume_parameters: 
    794817        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)) 
    796824    else: 
    797825        # Model doesn't have volume.  We could make the kernel run a little 
    798826        # faster by not using/transferring the volume normalizations, but 
    799827        # 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)" 
    801829    source.append(call_volume) 
    802  
     830    source.append(call_effective_radius) 
    803831    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" 
    806841    if xy_mode == 'qabc': 
    807842        pars = ",".join(["_qa", "_qb", "_qc"] + model_refs) 
     
    812847        call_iqxy = "#define CALL_IQ_AC(_qa,_qc,_v) Iqac(%s)" % pars 
    813848        clear_iqxy = "#undef CALL_IQ_AC" 
    814     elif xy_mode == 'qa': 
     849    elif xy_mode == 'qa' and not model_info.have_Fq: 
    815850        pars = ",".join(["_qa"] + model_refs) 
    816851        call_iqxy = "#define CALL_IQ_A(_qa,_v) Iq(%s)" % pars 
    817852        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" 
    818861    elif xy_mode == 'qxy': 
    819862        orientation_refs = _call_pars("_v.", partable.orientation_parameters) 
     
    831874    magpars = [k-2 for k, p in enumerate(partable.call_parameters) 
    832875               if p.type == 'sld'] 
    833  
    834876    # Fill in definitions for numbers of parameters 
    835877    source.append("#define MAX_PD %s"%partable.max_pd) 
     
    839881    source.append("#define MAGNETIC_PARS %s"%",".join(str(k) for k in magpars)) 
    840882    source.append("#define PROJECTION %d"%PROJECTION) 
    841  
    842883    # 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 
    846887    result = { 
    847888        'dll': '\n'.join(source+dll[0]+dll[1]+dll[2]), 
    848889        'opencl': '\n'.join(source+ocl[0]+ocl[1]+ocl[2]), 
    849890    } 
    850  
    851891    return result 
    852892 
    853893 
    854 def _kernels(kernel, call_iq, call_iqxy, clear_iqxy, name): 
     894def _kernels(kernel, call_iq, clear_iq, call_iqxy, clear_iqxy, name): 
    855895    # type: ([str,str], str, str, str) -> List[str] 
    856896    code = kernel[0] 
     
    862902        '#line 1 "%s Iq"' % path, 
    863903        code, 
    864         "#undef CALL_IQ", 
     904        clear_iq, 
    865905        "#undef KERNEL_NAME", 
    866906        ] 
Note: See TracChangeset for help on using the changeset viewer.