Changeset a5b8477 in sasmodels
- Timestamp:
- Apr 13, 2016 6:17:10 PM (9 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, release_v0.94, release_v0.95, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- 0ce5710
- Parents:
- 60f03de
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
doc/genapi.py
r3d5c6f8 ra5b8477 59 59 #('alignment', 'GPU data alignment [unused]'), 60 60 ('bumps_model', 'Bumps interface'), 61 ('compare', 'Compare models on different compute engines'), 61 62 ('convert', 'Sasview to sasmodel converter'), 62 63 ('core', 'Model access'), 64 ('data', 'Data layout and plotting routines'), 65 ('details', 'Parameter packing for kernel calls'), 63 66 ('direct_model', 'Simple interface'), 64 67 ('exception', 'Annotate exceptions'), 68 #('frozendict', 'Freeze a dictionary to make it immutable'), 65 69 ('generate', 'Model parser'), 70 ('kernel', 'Evaluator type definitions'), 66 71 ('kernelcl', 'OpenCL model evaluator'), 67 72 ('kerneldll', 'Ctypes model evaluator'), 68 73 ('kernelpy', 'Python model evaluator'), 74 ('list_pars', 'Identify all parameters in all models'), 75 ('mixture', 'Mixture model evaluator'), 69 76 ('model_test', 'Unit test support'), 77 ('modelinfo', 'Parameter and model definitions'), 78 ('product', 'Product model evaluator'), 70 79 ('resolution', '1-D resolution functions'), 71 80 ('resolution2d', '2-D resolution functions'), 72 81 ('sasview_model', 'Sasview interface'), 73 ('sesans', 'SESANS model evaluator'), 82 ('sesans', 'SESANS calculation routines'), 83 #('transition', 'Model stepper for automatic model selection'), 74 84 ('weights', 'Distribution functions'), 75 #('transition', 'Model stepper for automatic model selection'),76 85 ] 77 86 package='sasmodels' -
doc/genmodel.py
r89f4163 ra5b8477 1 from __future__ import print_function 2 1 3 import sys, os, math, re 2 4 import numpy as np 3 5 import matplotlib.pyplot as plt 4 import pylab5 6 sys.path.insert(0, os.path.abspath('..')) 6 7 from sasmodels import generate, core … … 8 9 from sasmodels.data import empty_data1D, empty_data2D 9 10 10 11 # Convert ../sasmodels/models/name.py to name 12 model_name = os.path.basename(sys.argv[1])[:-3] 13 model_info = core.load_model_info(model_name) 14 model = core.build_model(model_info) 15 16 # Load the doc string from the module definition file and store it in rst 17 docstr = generate.make_doc(model_info) 18 19 20 # Calculate 1D curve for default parameters 21 pars = dict((p.name, p.default) for p in model_info['parameters']) 22 23 # Plotting ranges and options 24 opts = { 25 'xscale' : 'log', 26 'yscale' : 'log' if not model_info['structure_factor'] else 'linear', 27 'zscale' : 'log' if not model_info['structure_factor'] else 'linear', 28 'q_min' : 0.001, 29 'q_max' : 1.0, 30 'nq' : 1000, 31 'nq2d' : 1000, 32 'vmin' : 1e-3, # floor for the 2D data results 33 'qx_max' : 0.5, 34 #'colormap' : 'gist_ncar', 35 'colormap' : 'nipy_spectral', 36 #'colormap' : 'jet', 37 } 38 11 try: 12 from typing import Dict, Any 13 except ImportError: 14 pass 15 else: 16 from matplotlib.axes import Axes 17 from sasmodels.kernel import KernelModel 18 from sasmodels.modelinfo import ModelInfo 39 19 40 20 def plot_1d(model, opts, ax): 21 # type: (KernelModel, Dict[str, Any], Axes) -> None 22 """ 23 Create a 1-D image. 24 """ 41 25 q_min, q_max, nq = opts['q_min'], opts['q_max'], opts['nq'] 42 26 q_min = math.log10(q_min) … … 47 31 Iq1D = calculator() 48 32 49 ax.plot(q, Iq1D, color='blue', lw=2, label=model _info['name'])33 ax.plot(q, Iq1D, color='blue', lw=2, label=model.info.name) 50 34 ax.set_xlabel(r'$Q \/(\AA^{-1})$') 51 35 ax.set_ylabel(r'$I(Q) \/(\mathrm{cm}^{-1})$') … … 55 39 56 40 def plot_2d(model, opts, ax): 41 # type: (KernelModel, Dict[str, Any], Axes) -> None 42 """ 43 Create a 2-D image. 44 """ 57 45 qx_max, nq2d = opts['qx_max'], opts['nq2d'] 58 q = np.linspace(-qx_max, qx_max, nq2d) 46 q = np.linspace(-qx_max, qx_max, nq2d) # type: np.ndarray 59 47 data2d = empty_data2D(q, resolution=0.0) 60 48 calculator = DirectModel(data2d, model) … … 64 52 Iq2D = np.log(np.clip(Iq2D, opts['vmin'], np.inf)) 65 53 ax.imshow(Iq2D, interpolation='nearest', aspect=1, origin='lower', 66 extent=[-qx_max, qx_max, -qx_max, qx_max], cmap=opts['colormap'])54 extent=[-qx_max, qx_max, -qx_max, qx_max], cmap=opts['colormap']) 67 55 ax.set_xlabel(r'$Q_x \/(\AA^{-1})$') 68 56 ax.set_ylabel(r'$Q_y \/(\AA^{-1})$') 69 57 70 # Generate image 71 fig_height = 3.0 # in 72 fig_left = 0.6 # in 73 fig_right = 0.5 # in 74 fig_top = 0.6*0.25 # in 75 fig_bottom = 0.6*0.75 76 if model_info['has_2d']: 77 plot_height = fig_height - (fig_top+fig_bottom) 78 plot_width = plot_height 79 fig_width = 2*(plot_width + fig_left + fig_right) 80 aspect = (fig_width, fig_height) 81 ratio = aspect[0]/aspect[1] 82 ax_left = fig_left/fig_width 83 ax_bottom = fig_bottom/fig_height 84 ax_height = plot_height/fig_height 85 ax_width = ax_height/ratio # square axes 86 fig = plt.figure(figsize=aspect) 87 ax2d = fig.add_axes([0.5+ax_left, ax_bottom, ax_width, ax_height]) 88 plot_2d(model, opts, ax2d) 89 ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height]) 90 plot_1d(model, opts, ax1d) 91 #ax.set_aspect('square') 92 else: 93 plot_height = fig_height - (fig_top+fig_bottom) 94 plot_width = (1+np.sqrt(5))/2*fig_height 95 fig_width = plot_width + fig_left + fig_right 96 ax_left = fig_left/fig_width 97 ax_bottom = fig_bottom/fig_height 98 ax_width = plot_width/fig_width 99 ax_height = plot_height/fig_height 100 aspect = (fig_width, fig_height) 101 fig = plt.figure(figsize=aspect) 102 ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height]) 103 plot_1d(model, opts, ax1d) 58 def figfile(model_info): 59 # type: (ModelInfo) -> str 60 return model_info.id + '_autogenfig.png' 104 61 105 # Save image in model/img 106 figname = model_name + '_autogenfig.png' 107 filename = os.path.join('model', 'img', figname) 108 plt.savefig(filename, bbox_inches='tight') 109 #print "figure saved in",filename 62 def make_figure(model_info, opts): 63 # type: (ModelInfo, Dict[str, Any]) -> None 64 """ 65 Generate the figure file to include in the docs. 66 """ 67 model = core.build_model(model_info) 110 68 111 # Auto caption for figure 112 captionstr = '\n' 113 captionstr += '.. figure:: img/' + model_info['id'] + '_autogenfig.png\n' 114 captionstr += '\n' 115 if model_info['has_2d']: 116 captionstr += ' 1D and 2D plots corresponding to the default parameters of the model.\n' 117 else: 118 captionstr += ' 1D plot corresponding to the default parameters of the model.\n' 119 captionstr += '\n' 69 fig_height = 3.0 # in 70 fig_left = 0.6 # in 71 fig_right = 0.5 # in 72 fig_top = 0.6*0.25 # in 73 fig_bottom = 0.6*0.75 74 if model_info.parameters.has_2d: 75 plot_height = fig_height - (fig_top+fig_bottom) 76 plot_width = plot_height 77 fig_width = 2*(plot_width + fig_left + fig_right) 78 aspect = (fig_width, fig_height) 79 ratio = aspect[0]/aspect[1] 80 ax_left = fig_left/fig_width 81 ax_bottom = fig_bottom/fig_height 82 ax_height = plot_height/fig_height 83 ax_width = ax_height/ratio # square axes 84 fig = plt.figure(figsize=aspect) 85 ax2d = fig.add_axes([0.5+ax_left, ax_bottom, ax_width, ax_height]) 86 plot_2d(model, opts, ax2d) 87 ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height]) 88 plot_1d(model, opts, ax1d) 89 #ax.set_aspect('square') 90 else: 91 plot_height = fig_height - (fig_top+fig_bottom) 92 plot_width = (1+np.sqrt(5))/2*fig_height 93 fig_width = plot_width + fig_left + fig_right 94 ax_left = fig_left/fig_width 95 ax_bottom = fig_bottom/fig_height 96 ax_width = plot_width/fig_width 97 ax_height = plot_height/fig_height 98 aspect = (fig_width, fig_height) 99 fig = plt.figure(figsize=aspect) 100 ax1d = fig.add_axes([ax_left, ax_bottom, ax_width, ax_height]) 101 plot_1d(model, opts, ax1d) 120 102 121 # Add figure reference and caption to documentation (at end, before References) 122 pattern = '\*\*REFERENCE' 123 m = re.search(pattern, docstr.upper()) 103 # Save image in model/img 104 path = os.path.join('model', 'img', figfile(model_info)) 105 plt.savefig(path, bbox_inches='tight') 106 #print("figure saved in",path) 124 107 125 if m: 126 docstr1 = docstr[:m.start()] 127 docstr2 = docstr[m.start():] 128 docstr = docstr1 + captionstr + docstr2 129 else: 130 print '------------------------------------------------------------------' 131 print 'References NOT FOUND for model: ', model_info['id'] 132 print '------------------------------------------------------------------' 133 docstr = docstr + captionstr 108 def gen_docs(model_info): 109 # type: (ModelInfo) -> None 110 """ 111 Generate the doc string with the figure inserted before the references. 112 """ 134 113 135 open(sys.argv[2],'w').write(docstr) 114 # Load the doc string from the module definition file and store it in rst 115 docstr = generate.make_doc(model_info) 116 117 # Auto caption for figure 118 captionstr = '\n' 119 captionstr += '.. figure:: img/' + figfile(model_info) + '\n' 120 captionstr += '\n' 121 if model_info.parameters.has_2d: 122 captionstr += ' 1D and 2D plots corresponding to the default parameters of the model.\n' 123 else: 124 captionstr += ' 1D plot corresponding to the default parameters of the model.\n' 125 captionstr += '\n' 126 127 # Add figure reference and caption to documentation (at end, before References) 128 pattern = '\*\*REFERENCE' 129 match = re.search(pattern, docstr.upper()) 130 131 if match: 132 docstr1 = docstr[:match.start()] 133 docstr2 = docstr[match.start():] 134 docstr = docstr1 + captionstr + docstr2 135 else: 136 print('------------------------------------------------------------------') 137 print('References NOT FOUND for model: ', model_info.id) 138 print('------------------------------------------------------------------') 139 docstr += captionstr 140 141 open(sys.argv[2],'w').write(docstr) 142 143 def process_model(path): 144 # type: (str) -> None 145 """ 146 Generate doc file and image file for the given model definition file. 147 """ 148 149 # Load the model file 150 model_name = os.path.basename(path)[:-3] 151 model_info = core.load_model_info(model_name) 152 153 # Plotting ranges and options 154 opts = { 155 'xscale' : 'log', 156 'yscale' : 'log' if not model_info.structure_factor else 'linear', 157 'zscale' : 'log' if not model_info.structure_factor else 'linear', 158 'q_min' : 0.001, 159 'q_max' : 1.0, 160 'nq' : 1000, 161 'nq2d' : 1000, 162 'vmin' : 1e-3, # floor for the 2D data results 163 'qx_max' : 0.5, 164 #'colormap' : 'gist_ncar', 165 'colormap' : 'nipy_spectral', 166 #'colormap' : 'jet', 167 } 168 169 # Generate the RST file and the figure. Order doesn't matter. 170 gen_docs(model_info) 171 make_figure(model_info, opts) 172 173 if __name__ == "__main__": 174 process_model(sys.argv[1]) -
doc/gentoc.py
r5041682 ra5b8477 9 9 from sasmodels.core import load_model_info 10 10 11 try: 12 from typing import Optional, BinaryIO, List, Dict 13 except ImportError: 14 pass 15 else: 16 from sasmodels.modelinfo import ModelInfo 11 17 12 18 TEMPLATE="""\ … … 27 33 28 34 def _make_category(category_name, label, title, parent=None): 35 # type: (str, str, str, Optional[BinaryIO]) -> BinaryIO 29 36 file = open(joinpath(MODEL_TOC_PATH, category_name+".rst"), "w") 30 37 file.write(TEMPLATE%{'label':label, 'title':title, 'bar':'*'*len(title)}) … … 34 41 35 42 def _add_subcategory(category_name, parent): 43 # type: (str, BinaryIO) -> None 36 44 parent.write(" %s.rst\n"%category_name) 37 45 38 46 def _add_model(file, model_name): 47 # type: (IO[str], str) -> None 39 48 file.write(" ../../model/%s.rst\n"%model_name) 40 49 41 50 def _maybe_make_category(category, models, cat_files, model_toc): 51 # type: (str, List[str], Dict[str, BinaryIO], BinaryIO) -> None 42 52 if category not in cat_files: 43 53 print("Unexpected category %s containing"%category, models, file=sys.stderr) … … 46 56 47 57 def generate_toc(model_files): 58 # type: (List[str]) -> None 48 59 if not model_files: 49 60 print("gentoc needs a list of model files", file=sys.stderr) 50 61 51 62 # find all categories 52 category = {} 63 category = {} # type: Dict[str, List[str]] 53 64 for item in model_files: 54 65 # assume model is in sasmodels/models/name.py, and ignore the full path … … 56 67 if model_name.startswith('_'): continue 57 68 model_info = load_model_info(model_name) 58 if model_info ['category']is None:69 if model_info.category is None: 59 70 print("Missing category for", item, file=sys.stderr) 60 71 else: 61 category.setdefault(model_info ['category'],[]).append(model_name)72 category.setdefault(model_info.category,[]).append(model_name) 62 73 63 74 # Check category names -
sasmodels/data.py
r7ae2b7f ra5b8477 37 37 import numpy as np # type: ignore 38 38 39 try: 40 from typing import Union, Dict, List, Optional 41 except ImportError: 42 pass 43 else: 44 Data = Union["Data1D", "Data2D", "SesansData"] 45 39 46 def load_data(filename): 47 # type: (str) -> Data 40 48 """ 41 49 Load data using a sasview loader. … … 50 58 51 59 def set_beam_stop(data, radius, outer=None): 60 # type: (Data, float, Optional[float]) -> None 52 61 """ 53 62 Add a beam stop of the given *radius*. If *outer*, make an annulus. … … 65 74 66 75 def set_half(data, half): 76 # type: (Data, str) -> None 67 77 """ 68 78 Select half of the data, either "right" or "left". … … 78 88 79 89 def set_top(data, cutoff): 90 # type: (Data, float) -> None 80 91 """ 81 92 Chop the top off the data, above *cutoff*. … … 114 125 """ 115 126 def __init__(self, x=None, y=None, dx=None, dy=None): 127 # type: (Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]) -> None 116 128 self.x, self.y, self.dx, self.dy = x, y, dx, dy 117 129 self.dxl = None … … 127 139 128 140 def xaxis(self, label, unit): 141 # type: (str, str) -> None 129 142 """ 130 143 set the x axis label and unit … … 134 147 135 148 def yaxis(self, label, unit): 149 # type: (str, str) -> None 136 150 """ 137 151 set the y axis label and unit … … 140 154 self._yunit = unit 141 155 142 156 class SesansData(Data1D): 157 def __init__(self, **kw): 158 Data1D.__init__(self, **kw) 159 self.lam = None # type: Optional[np.ndarray] 143 160 144 161 class Data2D(object): … … 175 192 """ 176 193 def __init__(self, x=None, y=None, z=None, dx=None, dy=None, dz=None): 194 # type: (Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray], Optional[np.ndarray]) -> None 177 195 self.qx_data, self.dqx_data = x, dx 178 196 self.qy_data, self.dqy_data = y, dy … … 197 215 198 216 def xaxis(self, label, unit): 217 # type: (str, str) -> None 199 218 """ 200 219 set the x axis label and unit … … 204 223 205 224 def yaxis(self, label, unit): 225 # type: (str, str) -> None 206 226 """ 207 227 set the y axis label and unit … … 211 231 212 232 def zaxis(self, label, unit): 233 # type: (str, str) -> None 213 234 """ 214 235 set the y axis label and unit … … 223 244 """ 224 245 def __init__(self, x=None, y=None, z=None): 246 # type: (float, float, Optional[float]) -> None 225 247 self.x, self.y, self.z = x, y, z 226 248 … … 230 252 """ 231 253 def __init__(self, pixel_size=(None, None), distance=None): 254 # type: (Tuple[float, float], float) -> None 232 255 self.pixel_size = Vector(*pixel_size) 233 256 self.distance = distance … … 238 261 """ 239 262 def __init__(self): 263 # type: () -> None 240 264 self.wavelength = np.NaN 241 265 self.wavelength_unit = "A" … … 243 267 244 268 def empty_data1D(q, resolution=0.0): 269 # type: (np.ndarray, float) -> Data1D 245 270 """ 246 271 Create empty 1D data using the given *q* as the x value. … … 259 284 260 285 def empty_data2D(qx, qy=None, resolution=0.0): 286 # type: (np.ndarray, Optional[np.ndarray], float) -> Data2D 261 287 """ 262 288 Create empty 2D data using the given mesh. … … 272 298 Qx, Qy = np.meshgrid(qx, qy) 273 299 Qx, Qy = Qx.flatten(), Qy.flatten() 274 Iq = 100 * np.ones_like(Qx) 300 Iq = 100 * np.ones_like(Qx) # type: np.ndarray 275 301 dIq = np.sqrt(Iq) 276 302 if resolution != 0: … … 300 326 301 327 def plot_data(data, view='log', limits=None): 328 # type: (Data, str, Optional[Tuple[float, float]]) -> None 302 329 """ 303 330 Plot data loaded by the sasview loader. … … 323 350 def plot_theory(data, theory, resid=None, view='log', 324 351 use_data=True, limits=None, Iq_calc=None): 352 # type: (Data, Optional[np.ndarray], Optional[np.ndarray], str, bool, Optional[Tuple[float,float]], Optional[np.ndarray]) -> None 325 353 """ 326 354 Plot theory calculation. … … 337 365 *limits* sets the intensity limits on the plot; if None then the limits 338 366 are inferred from the data. 367 368 *Iq_calc* is the raw theory values without resolution smearing 339 369 """ 340 370 if hasattr(data, 'lam'): … … 348 378 349 379 def protect(fn): 380 # type: (Callable) -> Callable 350 381 """ 351 382 Decorator to wrap calls in an exception trapper which prints the … … 367 398 def _plot_result1D(data, theory, resid, view, use_data, 368 399 limits=None, Iq_calc=None): 400 # type: (Data1D, Optional[np.ndarray], Optional[np.ndarray], str, bool, Optional[Tuple[float, float]], Optional[np.ndarray]) -> None 369 401 """ 370 402 Plot the data and residuals for 1D data. … … 444 476 @protect 445 477 def _plot_result_sesans(data, theory, resid, use_data, limits=None): 478 # type: (SesansData, Optional[np.ndarray], Optional[np.ndarray], bool, Optional[Tuple[float, float]]) -> None 446 479 """ 447 480 Plot SESANS results. … … 454 487 455 488 if use_data or use_theory: 456 is_tof = np.any(data.lam!=data.lam[0])489 is_tof = (data.lam != data.lam[0]).any() 457 490 if num_plots > 1: 458 491 plt.subplot(1, num_plots, 1) 459 492 if use_data: 460 493 if is_tof: 461 plt.errorbar(data.x, np.log(data.y)/(data.lam*data.lam), yerr=data.dy/data.y/(data.lam*data.lam)) 494 plt.errorbar(data.x, np.log(data.y)/(data.lam*data.lam), 495 yerr=data.dy/data.y/(data.lam*data.lam)) 462 496 else: 463 497 plt.errorbar(data.x, data.y, yerr=data.dy) … … 487 521 @protect 488 522 def _plot_result2D(data, theory, resid, view, use_data, limits=None): 523 # type: (Data2D, Optional[np.ndarray], Optional[np.ndarray], str, bool, Optional[Tuple[float,float]]) -> None 489 524 """ 490 525 Plot the data and residuals for 2D data. … … 498 533 # Put theory and data on a common colormap scale 499 534 vmin, vmax = np.inf, -np.inf 535 target = None # type: Optional[np.ndarray] 500 536 if use_data: 501 537 target = data.data[~data.mask] … … 546 582 @protect 547 583 def _plot_2d_signal(data, signal, vmin=None, vmax=None, view='log'): 584 # type: (Data2D, np.ndarray, Optional[float], Optional[float], str) -> Tuple[float, float] 548 585 """ 549 586 Plot the target value for the data. This could be the data itself, … … 587 624 588 625 def demo(): 626 # type: () -> None 589 627 """ 590 628 Load and plot a SAS dataset. -
sasmodels/direct_model.py
r7ae2b7f ra5b8477 32 32 from . import details 33 33 34 35 def call_kernel(kernel, pars, cutoff=0, mono=False): 34 try: 35 from typing import Optional, Dict, Tuple 36 except ImportError: 37 pass 38 else: 39 from .data import Data 40 from .kernel import Kernel, KernelModel 41 from .modelinfo import Parameter, ParameterSet 42 43 def call_kernel(kernel, pars, cutoff=0., mono=False): 44 # type: (Kernel, ParameterSet, float, bool) -> np.ndarray 36 45 """ 37 46 Call *kernel* returned from *model.make_kernel* with parameters *pars*. … … 64 73 65 74 def get_weights(parameter, values): 75 # type: (Parameter, Dict[str, float]) -> Tuple[np.ndarray, np.ndarray] 66 76 """ 67 77 Generate the distribution for parameter *name* given the parameter values … … 106 116 """ 107 117 def _interpret_data(self, data, model): 118 # type: (Data, KernelModel) -> None 108 119 # pylint: disable=attribute-defined-outside-init 109 120 … … 206 217 207 218 def _set_data(self, Iq, noise=None): 219 # type: (np.ndarray, Optional[float]) -> None 208 220 # pylint: disable=attribute-defined-outside-init 209 221 if noise is not None: … … 223 235 224 236 def _calc_theory(self, pars, cutoff=0.0): 237 # type: (ParameterSet, float) -> np.ndarray 225 238 if self._kernel is None: 226 239 self._kernel = self._model.make_kernel(self._kernel_inputs) … … 258 271 """ 259 272 def __init__(self, data, model, cutoff=1e-5): 273 # type: (Data, KernelModel, float) -> None 260 274 self.model = model 261 275 self.cutoff = cutoff … … 264 278 265 279 def __call__(self, **pars): 280 # type: (**float) -> np.ndarray 266 281 return self._calc_theory(pars, cutoff=self.cutoff) 267 282 268 283 def simulate_data(self, noise=None, **pars): 284 # type: (Optional[float], **float) -> None 269 285 """ 270 286 Generate simulated data for the model. … … 274 290 275 291 def main(): 292 # type: () -> None 276 293 """ 277 294 Program to evaluate a particular model at a set of q values. -
sasmodels/generate.py
r7ae2b7f ra5b8477 123 123 polydispersity values for tests. 124 124 125 A n *model_info* dictionary is constructed from the kernel meta data and126 returned to the caller.125 A :class:`modelinfo.ModelInfo` structure is constructed from the kernel meta 126 data and returned to the caller. 127 127 128 128 The doc string at the start of the kernel module will be used to … … 132 132 file systems are case-sensitive, so only use lower case characters for 133 133 file names and extensions. 134 135 136 The function :func:`make` loads the metadata from the module and returns137 the kernel source. The function :func:`make_doc` extracts the doc string138 and adds the parameter table to the top. The function :func:`model_sources`139 returns a list of files required by the model.140 134 141 135 Code follows the C99 standard with the following extensions and conditions:: … … 149 143 all double declarations may be converted to half, float, or long double 150 144 FLOAT_SIZE is the number of bytes in the converted variables 145 146 :func:`load_kernel_module` loads the model definition file and 147 :modelinfo:`make_model_info` parses it. :func:`make_source` 148 converts C-based model definitions to C source code, including the 149 polydispersity integral. :func:`model_sources` returns the list of 150 source files the model depends on, and :func:`timestamp` returns 151 the latest time stamp amongst the source files (so you can check if 152 the model needs to be rebuilt). 153 154 The function :func:`make_doc` extracts the doc string and adds the 155 parameter table to the top. *make_figure* in *sasmodels/doc/genmodel* 156 creates the default figure for the model. [These two sets of code 157 should mignrate into docs.py so docs can be updated in one place]. 151 158 """ 152 159 from __future__ import print_function … … 585 592 Sq_units = "The returned value is a dimensionless structure factor, $S(q)$." 586 593 docs = convert_section_titles_to_boldface(model_info.docs) 594 pars = make_partable(model_info.parameters.COMMON 595 + model_info.parameters.kernel_parameters) 587 596 subst = dict(id=model_info.id.replace('_', '-'), 588 597 name=model_info.name, 589 598 title=model_info.title, 590 parameters= make_partable(model_info.parameters.kernel_parameters),599 parameters=pars, 591 600 returns=Sq_units if model_info.structure_factor else Iq_units, 592 601 docs=docs) -
sasmodels/kernel.py
r04dc697 ra5b8477 20 20 class KernelModel(object): 21 21 info = None # type: ModelInfo 22 dtype = None # type: np.dtype 22 23 def make_kernel(self, q_vectors): 23 24 # type: (List[np.ndarray]) -> "Kernel" -
sasmodels/kernelcl.py
r7ae2b7f ra5b8477 49 49 """ 50 50 from __future__ import print_function 51 51 52 import os 52 53 import warnings … … 68 69 from . import generate 69 70 from .kernel import KernelModel, Kernel 71 72 try: 73 from typing import Tuple, Callable, Any 74 from .modelinfo import ModelInfo 75 from .details import CallDetails 76 except ImportError: 77 pass 70 78 71 79 # The max loops number is limited by the amount of local memory available … … 441 449 Call :meth:`release` when done with the kernel instance. 442 450 """ 443 def __init__(self, kernel, model_info, q_vectors, dtype): 444 max_pd = model_info.max_pd 445 npars = len(model_info.parameters)-2 446 q_input = GpuInput(q_vectors, dtype) 447 self.dtype = dtype 448 self.dim = '2d' if q_input.is_2d else '1d' 451 def __init__(self, kernel, model_info, q_vectors): 452 # type: (KernelModel, ModelInfo, List[np.ndarray]) -> None 453 max_pd = model_info.parameters.max_pd 454 npars = len(model_info.parameters.kernel_parameters)-2 455 q_input = GpuInput(q_vectors, kernel.dtype) 449 456 self.kernel = kernel 450 457 self.info = model_info 458 self.dtype = kernel.dtype 459 self.dim = '2d' if q_input.is_2d else '1d' 451 460 self.pd_stop_index = 4*max_pd-1 452 461 # plus three for the normalization values … … 456 465 # Note: res may be shorter than res_b if global_size != nq 457 466 env = environment() 458 self.queue = env.get_queue( dtype)467 self.queue = env.get_queue(kernel.dtype) 459 468 460 469 # details is int32 data, padded to an 8 integer boundary 461 470 size = ((max_pd*5 + npars*3 + 2 + 7)//8)*8 462 471 self.result_b = cl.Buffer(self.queue.context, mf.READ_WRITE, 463 q_input.global_size[0] * q_input.dtype.itemsize)472 q_input.global_size[0] * kernel.dtype.itemsize) 464 473 self.q_input = q_input # allocated by GpuInput above 465 474 … … 467 476 468 477 def __call__(self, call_details, weights, values, cutoff): 478 # type: (CallDetails, np.ndarray, np.ndarray, float) -> np.ndarray 469 479 real = (np.float32 if self.q_input.dtype == generate.F32 470 480 else np.float64 if self.q_input.dtype == generate.F64 -
sasmodels/kerneldll.py
r7ae2b7f ra5b8477 55 55 56 56 from . import generate 57 from . import details58 57 from .kernel import KernelModel, Kernel 59 58 from .kernelpy import PyInput … … 279 278 self.dtype = q_input.dtype 280 279 self.dim = '2d' if q_input.is_2d else '1d' 281 self.result = np.empty(q_input.nq+3, q_input.dtype) 280 self.result = np.empty(q_input.nq+1, q_input.dtype) 281 self.real = (np.float32 if self.q_input.dtype == generate.F32 282 else np.float64 if self.q_input.dtype == generate.F64 283 else np.float128) 282 284 283 285 def __call__(self, call_details, weights, values, cutoff): 284 286 # type: (CallDetails, np.ndarray, np.ndarray, float) -> np.ndarray 285 real = (np.float32 if self.q_input.dtype == generate.F32 286 else np.float64 if self.q_input.dtype == generate.F64 287 else np.float128) 288 assert isinstance(call_details, details.CallDetails) 289 assert weights.dtype == real and values.dtype == real 290 291 start, stop = 0, call_details.total_pd 287 292 288 #print("in kerneldll") 293 289 #print("weights", weights) 294 290 #print("values", values) 291 start, stop = 0, call_details.total_pd 295 292 args = [ 296 293 self.q_input.nq, # nq … … 302 299 self.q_input.q.ctypes.data, #q 303 300 self.result.ctypes.data, # results 304 real(cutoff), # cutoff301 self.real(cutoff), # cutoff 305 302 ] 306 303 self.kernel(*args) # type: ignore 307 return self.result[:- 3]304 return self.result[:-1] 308 305 309 306 def release(self): -
sasmodels/modelinfo.py
r60f03de ra5b8477 103 103 limits = (float(low), float(high)) 104 104 except Exception: 105 print("user_limits",user_limits) 105 106 raise ValueError("invalid limits for %s"%name) 106 107 else: … … 278 279 15 degrees). 279 280 280 In the usual process these values are set by :func:`make_parameter_table`281 and:func:`parse_parameter` therein.281 These values are set by :func:`make_parameter_table` and 282 :func:`parse_parameter` therein. 282 283 """ 283 284 def __init__(self, name, units='', default=None, limits=(-np.inf, np.inf), … … 644 645 info.structure_factor = getattr(kernel_module, 'structure_factor', False) 645 646 info.profile_axes = getattr(kernel_module, 'profile_axes', ['x', 'y']) 646 info.variant_info = getattr(kernel_module, 'variant_info', None)647 647 info.source = getattr(kernel_module, 'source', []) 648 648 # TODO: check the structure of the tests … … 670 670 if the name is in a string. 671 671 672 The *model_info* structure contains the following fields:673 674 * *id* is the id of the kernel675 * *name* is the display name of the kernel676 * *filename* is the full path to the module defining the file (if any)677 * *title* is a short description of the kernel678 * *description* is a long description of the kernel (this doesn't seem679 very useful since the Help button on the model page brings you directly680 to the documentation page)681 * *docs* is the docstring from the module. Use :func:`make_doc` to682 * *category* specifies the model location in the docs683 * *parameters* is the model parameter table684 * *single* is True if the model allows single precision685 * *structure_factor* is True if the model is useable in a product686 * *variant_info* contains the information required to select between687 model variants (e.g., the list of cases) or is None if there are no688 model variants689 * *par_type* categorizes the model parameters. See690 :func:`categorize_parameters` for details.691 * *demo* contains the *{parameter: value}* map used in compare (and maybe692 for the demo plot, if plots aren't set up to use the default values).693 If *demo* is not given in the file, then the default values will be used.694 * *tests* is a set of tests that must pass695 * *source* is the list of library files to include in the C model build696 * *Iq*, *Iqxy*, *form_volume*, *ER*, *VR* and *sesans* are python functions697 implementing the kernel for the module, or None if they are not698 defined in python699 * *composition* is None if the model is independent, otherwise it is a700 tuple with composition type ('product' or 'mixture') and a list of701 *model_info* blocks for the composition objects. This allows us to702 build complete product and mixture models from just the info.703 * *control* is the name of the control parameter if there is one.704 * *hidden* returns the list of hidden parameters given the value of the705 control parameter706 707 672 The structure should be mostly static, other than the delayed definition 708 673 of *Iq* and *Iqxy* if they need to be defined. 709 674 """ 675 #: Full path to the file defining the kernel, if any. 676 filename = None # type: Optiona[str] 677 #: Id of the kernel used to load it from the filesystem. 710 678 id = None # type: str 711 filename = None # type: str 679 #: Display name of the model, which defaults to the model id but with 680 #: capitalization of the parts so for example core_shell defaults to 681 #: "Core Shell". 712 682 name = None # type: str 683 #: Short description of the model. 713 684 title = None # type: str 685 #: Long description of the model. 714 686 description = None # type: str 687 #: Model parameter table. Parameters are defined using a list of parameter 688 #: definitions, each of which is contains parameter name, units, 689 #: default value, limits, type and description. See :class:`Parameter` 690 #: for details on the individual parameters. The parameters are gathered 691 #: into a :class:`ParameterTable`, which provides various views into the 692 #: parameter list. 715 693 parameters = None # type: ParameterTable 694 #: Demo parameters as a *parameter:value* map used as the default values 695 #: for :mod:`compare`. Any parameters not set in *demo* will use the 696 #: defaults from the parameter table. That means no polydispersity, and 697 #: in the case of multiplicity models, a minimal model with no interesting 698 #: scattering. 716 699 demo = None # type: Dict[str, float] 700 #: Composition is None if this is an independent model, or it is a 701 #: tuple with comoposition type ('product' or 'misture') and a list of 702 #: :class:`ModelInfo` blocks for the composed objects. This allows us 703 #: to rebuild a complete mixture or product model from the info block. 704 #: *composition* is not given in the model definition file, but instead 705 #: arises when the model is constructed using names such as 706 #: *sphere*hardsphere* or *cylinder+sphere*. 717 707 composition = None # type: Optional[Tuple[str, List[ModelInfo]]] 708 #: Name of the control parameter for a variant model such as :ref:`rpa`. 709 #: The *control* parameter should appear in the parameter table, with 710 #: limits defined as *[CASES]*, for case names such as 711 #: *CASES = ["diblock copolymer", "triblock copolymer", ...]*. 712 #: This should give *limits=[[case1, case2, ...]]*, but the 713 #: model loader translates this to *limits=[0, len(CASES)-1]*, and adds 714 #: *choices=CASES* to the :class:`Parameter` definition. Note that 715 #: models can use a list of cases as a parameter without it being a 716 #: control parameter. Either way, the parameter is sent to the model 717 #: evaluator as *float(choice_num)*, where choices are numbered from 0. 718 #: See also :attr:`hidden`. 718 719 control = None # type: str 720 #: Different variants require different parameters. In order to show 721 #: just the parameters needed for the variant selected by :attr:`control`, 722 #: you should provide a function *hidden(control) -> set(['a', 'b', ...])* 723 #: indicating which parameters need to be hidden. For multiplicity 724 #: models, you need to use the complete name of the parameter, including 725 #: its number. So for example, if variant "a" uses only *sld1* and *sld2*, 726 #: then *sld3*, *sld4* and *sld5* of multiplicity parameter *sld[5]* 727 #: should be in the hidden set. 728 hidden = None # type: Optional[Callable[[int], Set[str]]] 729 #: Doc string from the top of the model file. This should be formatted 730 #: using ReStructuredText format, with latex markup in ".. math" 731 #: environments, or in dollar signs. This will be automatically 732 #: extracted to a .rst file by :func:`generate.make_docs`, then 733 #: converted to HTML or PDF by Sphinx. 719 734 docs = None # type: str 735 #: Location of the model description in the documentation. This takes the 736 #: form of "section" or "section:subsection". So for example, 737 #: :ref:`porod` uses *category="shape-independent"* so it is in the 738 #: :ref:`Shape-independent` section whereas 739 #: :ref:`capped_cylinder` uses: *category="shape:cylinder"*, which puts 740 #: it in the :ref:`shape-cylinder` section. 720 741 category = None # type: Optional[str] 742 #: True if the model can be computed accurately with single precision. 743 #: This is True by default, but models such as :ref:`bcc_paracrystal` set 744 #: it to False because they require double precision calculations. 721 745 single = None # type: bool 746 #: True if the model is a structure factor used to model the interaction 747 #: between form factor models. This will default to False if it is not 748 #: provided in the file. 722 749 structure_factor = None # type: bool 750 #: List of C source files used to define the model. The source files 751 #: should define the *Iq* function, and possibly *Iqxy*, though a default 752 #: *Iqxy = Iq(sqrt(qx**2+qy**2)* will be created if no *Iqxy* is provided. 753 #: Files containing the most basic functions must appear first in the list, 754 #: followed by the files that use those functions. Form factors are 755 #: indicated by providing a :attr:`ER` function. 756 source = None # type: List[str] 757 #: The set of tests that must pass. The format of the tests is described 758 #: in :mod:`model_test`. 759 tests = None # type: List[TestCondition] 760 #: Returns the effective radius of the model given its volume parameters. 761 #: The presence of *ER* indicates that the model is a form factor model 762 #: that may be used together with a structure factor to form an implicit 763 #: multiplication model. 764 #: 765 #: The parameters to the *ER* function must be marked with type *volume*. 766 #: in the parameter table. They will appear in the same order as they 767 #: do in the table. The values passed to *ER* will be vectors, with one 768 #: value for each polydispersity condition. For example, if the model 769 #: is polydisperse over both length and radius, then both length and 770 #: radius will have the same number of values in the vector, with one 771 #: value for each *length X radius*. If only *radius* is polydisperse, 772 #: then the value for *length* will be repeated once for each value of 773 #: *radius*. The *ER* function should return one effective radius for 774 #: each parameter set. Multiplicity parameters will be received as 775 #: arrays, with one row per polydispersity condition. 776 ER = None # type: Optional[Callable[[np.ndarray], np.ndarray]] 777 #: Returns the occupied volume and the total volume for each parameter set. 778 #: See :attr:`ER` for details on the parameters. 779 VR = None # type: Optional[Callable[[np.ndarray], Tuple[np.ndarray, np.ndarray]]] 780 #: Returns the form volume for python-based models. Form volume is needed 781 #: for volume normalization in the polydispersity integral. If no 782 #: parameters are *volume* parameters, then form volume is not needed. 783 #: For C-based models, (with :attr:`sources` defined, or with :attr:`Iq` 784 #: defined using a string containing C code), form_volume must also be 785 #: C code, either defined as a string, or in the sources. 786 form_volume = None # type: Union[None, str, Callable[[np.ndarray], float]] 787 #: Returns *I(q, a, b, ...)* for parameters *a*, *b*, etc. defined 788 #: by the parameter table. *Iq* can be defined as a python function, or 789 #: as a C function. If it is defined in C, then set *Iq* to the body of 790 #: the C function, including the return statement. This function takes 791 #: values for *q* and each of the parameters as separate *double* values 792 #: (which may be converted to float or long double by sasmodels). All 793 #: source code files listed in :attr:`sources` will be loaded before the 794 #: *Iq* function is defined. If *Iq* is not present, then sources should 795 #: define *static double Iq(double q, double a, double b, ...)* which 796 #: will return *I(q, a, b, ...)*. Multiplicity parameters are sent as 797 #: pointers to doubles. Constants in floating point expressions should 798 #: include the decimal point. See :mod:`generate` for more details. 799 Iq = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 800 #: Returns *I(qx, qy, a, b, ...)*. The interface follows :attr:`Iq`. 801 Iqxy = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 802 #: Returns a model profile curve *x, y*. If *profile* is defined, this 803 #: curve will appear in response to the *Show* button in SasView. Use 804 #: :attr:`profile_axes` to set the axis labels. Note that *y* values 805 #: will be scaled by 1e6 before plotting. 806 profile = None # type: Optional[Callable[[np.ndarray], None]] 807 #: Axis labels for the :attr:`profile` plot. The default is *['x', 'y']*. 808 #: Only the *x* component is used for now. 723 809 profile_axes = None # type: Tuple[str, str] 724 variant_info = None # type: Optional[List[str]] 725 source = None # type: List[str] 726 tests = None # type: List[TestCondition] 727 ER = None # type: Optional[Callable[[np.ndarray], np.ndarray]] 728 VR = None # type: Optional[Callable[[np.ndarray], Tuple[np.ndarray, np.ndarray]]] 729 form_volume = None # type: Union[None, str, Callable[[np.ndarray], float]] 730 Iq = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 731 Iqxy = None # type: Union[None, str, Callable[[np.ndarray], np.ndarray]] 732 profile = None # type: Optional[Callable[[np.ndarray], None]] 810 #: Returns *sesans(z, a, b, ...)* for models which can directly compute 811 #: the SESANS correlation function. Note: not currently implemented. 733 812 sesans = None # type: Optional[Callable[[np.ndarray], np.ndarray]] 734 hidden = None # type: Optional[Callable[[int], Set[str]]] 813 #: :class:details.CallDetails data for mono-disperse function evaluation. 814 #: This field is created automatically by the model loader, and should 815 #: not be defined as part of the model definition file. 735 816 mono_details = None # type: CallDetails 736 817 … … 740 821 741 822 def get_hidden_parameters(self, control): 823 """ 824 Returns the set of hidden parameters for the model. *control* is the 825 value of the control parameter. Note that multiplicity models have 826 an implicit control parameter, which is the parameter that controls 827 the multiplicity. 828 """ 742 829 if self.hidden is not None: 743 830 hidden = self.hidden(control) -
sasmodels/models/rpa.py
rea05c87 ra5b8477 86 86 # ["name", "units", default, [lower, upper], "type","description"], 87 87 parameters = [ 88 ["case_num", "", 1, CASES, "", "Component organization"],88 ["case_num", "", 1, [CASES], "", "Component organization"], 89 89 90 90 ["N[4]", "", 1000.0, [1, inf], "", "Degree of polymerization"],
Note: See TracChangeset
for help on using the changeset viewer.