def process_model(path): # type: (str) -> None """ Generate doc file and image file for the given model definition file. """ # Load the model file model_name = os.path.basename(path)[:-3] model_info = core.load_model_info(model_name) # Plotting ranges and options opts = { 'xscale' : 'log', 'yscale' : 'log' if not model_info.structure_factor else 'linear', 'zscale' : 'log' if not model_info.structure_factor else 'linear', 'q_min' : 0.001, 'q_max' : 1.0, 'nq' : 1000, 'nq2d' : 1000, 'vmin' : 1e-3, # floor for the 2D data results 'qx_max' : 0.5, #'colormap' : 'gist_ncar', 'colormap' : 'nipy_spectral', #'colormap' : 'jet', } # Generate the RST file and the figure. Order doesn't matter. gen_docs(model_info) make_figure(model_info, opts)
def process_model(path): # type: (str) -> None """ Generate doc file and image file for the given model definition file. """ # Load the model file model_name = os.path.basename(path)[:-3] model_info = core.load_model_info(model_name) # Plotting ranges and options opts = { 'xscale': 'log', 'yscale': 'log' if not model_info.structure_factor else 'linear', 'zscale': 'log' if not model_info.structure_factor else 'linear', 'q_min': 0.001, 'q_max': 1.0, 'nq': 1000, 'nq2d': 1000, 'vmin': 1e-3, # floor for the 2D data results 'qx_max': 0.5, #'colormap' : 'gist_ncar', 'colormap': 'nipy_spectral', #'colormap' : 'jet', } # Generate the RST file and the figure. Order doesn't matter. gen_docs(model_info) make_figure(model_info, opts)
def build_model(model_name, q, **pars): from sasmodels.core import load_model_info, build_model as build_sasmodel from sasmodels.data import empty_data1D from sasmodels.direct_model import DirectModel model_info = load_model_info(model_name) model = build_sasmodel(model_info, dtype='double!') data = empty_data1D(q) calculator = DirectModel(data, model, cutoff=0) calculator.pars = pars.copy() calculator.pars.setdefault('background', 0) return calculator
def process_model(py_file, force=False): # type: (str) -> None """ Generate doc file and image file for the given model definition file. Does nothing if the corresponding rst file is newer than *py_file*. Also checks the timestamp on the *genmodel.py* program (*__file__*), since we want to rerun the generator on all files if we change this code. If *force* then generate the rst file regardless of time stamps. """ rst_file = joinpath(TARGET_DIR, basename(py_file).replace('.py', '.rst')) if not (force or newer(py_file, rst_file) or newer(__file__, rst_file)): #print("skipping", rst_file) return rst_file # Load the model file model_info = core.load_model_info(py_file) if model_info.basefile is None: model_info.basefile = py_file # Plotting ranges and options PLOT_OPTS = { 'xscale': 'log', 'yscale': 'log' if not model_info.structure_factor else 'linear', 'zscale': 'log' if not model_info.structure_factor else 'linear', 'q_min': 0.001, 'q_max': 1.0, 'nq': 1000, 'nq2d': 1000, 'vmin': 1e-3, # floor for the 2D data results 'qx_max': 0.5, #'colormap' : 'gist_ncar', 'colormap': 'nipy_spectral', #'colormap' : 'jet', } # Generate the RST file and the figure. Order doesn't matter. print("generating", rst_file) gen_docs(model_info, rst_file) if force: make_figure(model_info, PLOT_OPTS) else: make_figure_cached(model_info, PLOT_OPTS) return rst_file
def _eval_demo_1d(resolution, title): import sys from sasmodels import core from sasmodels import direct_model name = sys.argv[1] if len(sys.argv) > 1 else 'cylinder' if name == 'cylinder': pars = {'length': 210, 'radius': 500, 'background': 0} elif name == 'teubner_strey': pars = {'a2': 0.003, 'c1': -1e4, 'c2': 1e10, 'background': 0.312643} elif name == 'sphere' or name == 'spherepy': pars = TEST_PARS_SLIT_SPHERE elif name == 'ellipsoid': pars = { 'scale': 0.05, 'background': 0, 'r_polar': 500, 'r_equatorial': 15000, 'sld': 6, 'sld_solvent': 1, } else: pars = {} model_info = core.load_model_info(name) model = core.build_model(model_info) kernel = model.make_kernel([resolution.q_calc]) theory = direct_model.call_kernel(kernel, pars) Iq = resolution.apply(theory) if isinstance(resolution, Slit1D): width, height = resolution.qx_width, resolution.qy_width Iq_romb = romberg_slit_1d(resolution.q, width, height, model, pars) else: dq = resolution.q_width Iq_romb = romberg_pinhole_1d(resolution.q, dq, model, pars) import matplotlib.pyplot as plt # type: ignore plt.loglog(resolution.q_calc, theory, label='unsmeared') plt.loglog(resolution.q, Iq, label='smeared', hold=True) plt.loglog(resolution.q, Iq_romb, label='romberg smeared', hold=True) plt.legend() plt.title(title) plt.xlabel("Q (1/Ang)") plt.ylabel("I(Q) (1/cm)")
def _eval_demo_1d(resolution, title): import sys from sasmodels import core name = sys.argv[1] if len(sys.argv) > 1 else 'cylinder' if name == 'cylinder': pars = {'length':210, 'radius':500, 'background': 0} elif name == 'teubner_strey': pars = {'a2':0.003, 'c1':-1e4, 'c2':1e10, 'background':0.312643} elif name == 'sphere' or name == 'spherepy': pars = TEST_PARS_SLIT_SPHERE elif name == 'ellipsoid': pars = { 'scale':0.05, 'background': 0, 'r_polar':500, 'r_equatorial':15000, 'sld':6, 'sld_solvent': 1, } else: pars = {} model_info = core.load_model_info(name) model = core.build_model(model_info) kernel = model.make_kernel([resolution.q_calc]) theory = core.call_kernel(kernel, pars) Iq = resolution.apply(theory) if isinstance(resolution, Slit1D): width, height = resolution.dqx, resolution.dqy Iq_romb = romberg_slit_1d(resolution.q, width, height, model, pars) else: dq = resolution.q_width Iq_romb = romberg_pinhole_1d(resolution.q, dq, model, pars) import matplotlib.pyplot as plt plt.loglog(resolution.q_calc, theory, label='unsmeared') plt.loglog(resolution.q, Iq, label='smeared', hold=True) plt.loglog(resolution.q, Iq_romb, label='romberg smeared', hold=True) plt.legend() plt.title(title) plt.xlabel("Q (1/Ang)") plt.ylabel("I(Q) (1/cm)")
def build_model(model_name, n=150, qmax=0.5, **pars): """ Build a calculator for the given shape. *model_name* is any sasmodels model. *n* and *qmax* define an n x n mesh on which to evaluate the model. The remaining parameters are stored in the returned calculator as *calculator.pars*. They are used by :func:`draw_scattering` to set the non-orientation parameters in the calculation. Returns a *calculator* function which takes a dictionary or parameters and produces Iqxy. The Iqxy value needs to be reshaped to an n x n matrix for plotting. See the :class:`sasmodels.direct_model.DirectModel` class for details. """ from sasmodels.core import load_model_info, build_model from sasmodels.data import empty_data2D from sasmodels.direct_model import DirectModel model_info = load_model_info(model_name) model = build_model(model_info) #, dtype='double!') q = np.linspace(-qmax, qmax, n) data = empty_data2D(q, q) calculator = DirectModel(data, model) # stuff the values for non-orientation parameters into the calculator calculator.pars = pars.copy() calculator.pars.setdefault('backgound', 1e-3) # fix the data limits so that we can see if the pattern fades # under rotation or angular dispersion Iqxy = calculator(theta=0, phi=0, psi=0, **calculator.pars) Iqxy = np.log(Iqxy) vmin, vmax = clipped_range(Iqxy, 0.95, mode='top') calculator.limits = vmin, vmax + 1 return calculator
def generate_toc(model_files): # type: (List[str]) -> None if not model_files: print("gentoc needs a list of model files", file=sys.stderr) # find all categories category = {} # type: Dict[str, List[str]] for item in model_files: # assume model is in sasmodels/models/name.py, and ignore the full path model_name = basename(item)[:-3] if model_name.startswith('_'): continue model_info = load_model_info(model_name) if model_info.category is None: print("Missing category for", item, file=sys.stderr) else: category.setdefault(model_info.category, []).append(model_name) # Check category names for k, v in category.items(): if len(v) == 1: print("Category %s contains only %s" % (k, v[0]), file=sys.stderr) # Generate category files for the table of contents. # Initially we had "shape functions" as an additional TOC level, but we # have revised it so that the individual shape categories now go at # the top level. Judicious rearrangement of comments will make the # "shape functions" level reappear. # We are forcing shape-independent, structure-factor and custom-models # to come at the end of the TOC. All other categories will come in # alphabetical order before them. if not exists(MODEL_TOC_PATH): mkdir(MODEL_TOC_PATH) model_toc = _make_category('index', 'Models', 'Model Functions') #shape_toc = _make_category( # 'shape', 'Shapes', 'Shape Functions', model_toc) free_toc = _make_category('shape-independent', 'Shape-independent', 'Shape-Independent Functions') struct_toc = _make_category('structure-factor', 'Structure-factor', 'Structure Factors') custom_toc = _make_category('custom-models', 'Custom-models', 'Custom Models') # remember to top level categories cat_files = { #'shape':shape_toc, 'shape': model_toc, 'shape-independent': free_toc, 'structure-factor': struct_toc, 'custom': custom_toc, } # Process the model lists for k, v in sorted(category.items()): if ':' in k: cat, subcat = k.split(':') _maybe_make_category(cat, v, cat_files, model_toc) cat_file = cat_files[cat] label = "-".join((cat, subcat)) filename = label title = subcat.capitalize() + " Functions" sub_toc = _make_category(filename, label, title, cat_file) for model in sorted(v): _add_model(sub_toc, model) sub_toc.close() else: _maybe_make_category(k, v, cat_files, model_toc) cat_file = cat_files[k] for model in sorted(v): _add_model(cat_file, model) #_add_subcategory('shape', model_toc) _add_subcategory('shape-independent', model_toc) _add_subcategory('structure-factor', model_toc) _add_subcategory('custom-models', model_toc) # Close the top-level category files #model_toc.close() for f in cat_files.values(): f.close()
import sys, os, math, re import numpy as np import matplotlib.pyplot as plt import pylab sys.path.insert(0, os.path.abspath('..')) from sasmodels import generate, core from sasmodels.direct_model import DirectModel from sasmodels.data import empty_data1D, empty_data2D # Convert ../sasmodels/models/name.py to name model_name = os.path.basename(sys.argv[1])[:-3] model_info = core.load_model_info(model_name) model = core.build_model(model_info) # Load the doc string from the module definition file and store it in rst docstr = generate.make_doc(model_info) # Calculate 1D curve for default parameters pars = dict((p.name, p.default) for p in model_info['parameters']) # Plotting ranges and options opts = { 'xscale': 'log', 'yscale': 'log' if not model_info['structure_factor'] else 'linear', 'zscale': 'log' if not model_info['structure_factor'] else 'linear', 'q_min': 0.001, 'q_max': 1.0, 'nq': 1000, 'nq2d': 1000, 'vmin': 1e-3, # floor for the 2D data results 'qx_max': 0.5,
def generate_toc(model_files): # type: (List[str]) -> None if not model_files: print("gentoc needs a list of model files", file=sys.stderr) # find all categories category = {} # type: Dict[str, List[str]] for item in model_files: # assume model is in sasmodels/models/name.py, and ignore the full path model_name = basename(item)[:-3] if model_name.startswith('_'): continue model_info = load_model_info(model_name) if model_info.category is None: print("Missing category for", item, file=sys.stderr) else: category.setdefault(model_info.category,[]).append(model_name) # Check category names for k,v in category.items(): if len(v) == 1: print("Category %s contains only %s"%(k,v[0]), file=sys.stderr) # Generate category files for the table of contents. # Initially we had "shape functions" as an additional TOC level, but we # have revised it so that the individual shape categories now go at # the top level. Judicious rearrangement of comments will make the # "shape functions" level reappear. # We are forcing shape-independent, structure-factor and custom-models # to come at the end of the TOC. All other categories will come in # alphabetical order before them. if not exists(MODEL_TOC_PATH): mkdir(MODEL_TOC_PATH) model_toc = _make_category( 'index', 'Models', 'Model Functions') #shape_toc = _make_category( # 'shape', 'Shapes', 'Shape Functions', model_toc) free_toc = _make_category( 'shape-independent', 'Shape-independent', 'Shape-Independent Functions') struct_toc = _make_category( 'structure-factor', 'Structure-factor', 'Structure Factors') custom_toc = _make_category( 'custom-models', 'Custom-models', 'Custom Models') # remember to top level categories cat_files = { #'shape':shape_toc, 'shape':model_toc, 'shape-independent':free_toc, 'structure-factor': struct_toc, 'custom': custom_toc, } # Process the model lists for k,v in sorted(category.items()): if ':' in k: cat,subcat = k.split(':') _maybe_make_category(cat, v, cat_files, model_toc) cat_file = cat_files[cat] label = "-".join((cat,subcat)) filename = label title = subcat.capitalize()+" Functions" sub_toc = _make_category(filename, label, title, cat_file) for model in sorted(v): _add_model(sub_toc, model) sub_toc.close() else: _maybe_make_category(k, v, cat_files, model_toc) cat_file = cat_files[k] for model in sorted(v): _add_model(cat_file, model) #_add_subcategory('shape', model_toc) _add_subcategory('shape-independent', model_toc) _add_subcategory('structure-factor', model_toc) _add_subcategory('custom-models', model_toc) # Close the top-level category files #model_toc.close() for f in cat_files.values(): f.close()
def gen_data(model_name, data, count=1, noise=2, mono=True, magnetic=False, cutoff=1e-5, maxdim=np.inf, precision='double'): r""" Generates the data for the given model and parameters. *model_name* is the name of the model. *data* is the data object giving $q, \Delta q$ calculation points. *N* is the number of comparisons to make. *cutoff* is the polydispersity weight cutoff to make the calculation a little bit faster. *maxdim* is maximum value for any shape dimension. *precision* is the name of the calculation engine to use. Returns iterator *(seed, pars, data), ...* where *pars* is *{par: value, ...}* and *data* is *(q, dq, iq, diq)*. """ is2d = False assert data.x.size > 0 model_info = sascore.load_model_info(model_name) calculator = sascomp.make_engine(model_info, data, precision, cutoff) default_pars = sascomp.get_pars(model_info) assert calculator._data.x.size > 0 x, dx = calculator._data.x, calculator._data.dx # A not very clean macro for evaluating the models, wich uses name and # seed from the current scope even though they haven't been defined yet. def simulate(pars): """ Generate a random dataset for *fn* evaluated at *pars*. Returns *(x, dx, y, dy)*, o. Note that this replaces the data object within *fn*. """ # TODO: support 2D data, which does not use x, dx, y, dy try: assert calculator._data.x.size > 0 calculator.simulate_data(noise=noise, **pars) assert calculator._data.x.size > 0 data = calculator._data # TODO: Do we need to copy? [Yes if data.y is reused.] result = (x, dx, data.y.copy(), data.dy.copy()) except Exception: traceback.print_exc() print(f"Error when generating {model_name} for {seed}") result = (x, dx, np.NaN * x, np.NaN * x) #raise return result def pretty(pars): """ Pretty the parameter set for displaying on one line """ parlist = sascomp.parlist(model_info, pars, is2d) parlist = parlist.replace(os.linesep, ' ') parlist = parlist.replace(': ', '=') return parlist t0 = -np.inf interval = 5 for k in range(count): seed = np.random.randint(int(1e6)) t1 = time.perf_counter() if t1 > t0 + interval: print(f"generating {model_name} {k+1} of {count}") t0 = t1 # Generate parameters with sascomp.push_seed(seed): pars = sascomp.randomize_pars(model_info, default_pars, maxdim) sascomp.constrain_pars(model_info, pars) if mono: pars = sascomp.suppress_pd(pars) if not magnetic: pars = sascomp.suppress_magnetism(pars) pars.update({'scale': 1, 'background': 1e-5}) #print(f"{model_name} {seed} {pretty(pars)}") # Evaluate model data = simulate(pars) # q, dq, iq, diq # Skip data sets with NaN or negative numbers. # Note: some datasets will have fewer entries than others. if np.isnan(data[2]).any(): print(f">>> NaN in {model_name} {seed} {pretty(pars)}") continue if (data[2] <= 0.).any(): print(f">>> Negative values in {model_name} {seed} {pretty(pars)}") continue yield seed, pars, data # TODO: can free the calculator now print(f"Complete {model_name}")