Changes in sasmodels/generate.py [4a4ae41:17bbadd] in sasmodels
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/generate.py
r4a4ae41 r17bbadd 117 117 are added to the beginning of the parameter list. They will show up 118 118 in the documentation as model parameters, but they are never sent to 119 the kernel functions. 120 121 *category* is the default category for the model. Models in the 122 *structure-factor* category do not have *scale* and *background* 123 added. 119 the kernel functions. Note that *effect_radius* and *volfraction* 120 must occur first in structure factor calculations. 121 122 *category* is the default category for the model. The category is 123 two level structure, with the form "group:section", indicating where 124 in the manual the model will be located. Models are alphabetical 125 within their section. 124 126 125 127 *source* is the list of C-99 source files that must be joined to … … 157 159 158 160 159 An * info* dictionary is constructed from the kernel meta data and161 An *model_info* dictionary is constructed from the kernel meta data and 160 162 returned to the caller. 161 163 … … 190 192 191 193 The function :func:`make` loads the metadata from the module and returns 192 the kernel source. The function :func:` doc` extracts the doc string194 the kernel source. The function :func:`make_doc` extracts the doc string 193 195 and adds the parameter table to the top. The function :func:`model_sources` 194 196 returns a list of files required by the model. … … 217 219 import numpy as np 218 220 219 #__all__ = ["make", "doc", "model_sources", "convert_type"] 221 #TODO: determine which functions are useful outside of generate 222 #__all__ = ["model_info", "make_doc", "make_source", "convert_type"] 220 223 221 224 C_KERNEL_TEMPLATE_PATH = joinpath(dirname(__file__), 'kernel_template.c') … … 327 330 raise ValueError("%r not found in %s" % (filename, search_path)) 328 331 329 def model_sources( info):332 def model_sources(model_info): 330 333 """ 331 334 Return a list of the sources file paths for the module. 332 335 """ 333 search_path = [dirname( info['filename']),336 search_path = [dirname(model_info['filename']), 334 337 abspath(joinpath(dirname(__file__), 'models'))] 335 return [_search(search_path, f) for f in info['source']]338 return [_search(search_path, f) for f in model_info['source']] 336 339 337 340 # Pragmas for enable OpenCL features. Be sure to protect them so that they … … 391 394 392 395 393 def kernel_name( info, is_2d):396 def kernel_name(model_info, is_2d): 394 397 """ 395 398 Name of the exported kernel symbol. 396 399 """ 397 return info['name'] + "_" + ("Iqxy" if is_2d else "Iq") 398 400 return model_info['name'] + "_" + ("Iqxy" if is_2d else "Iq") 401 402 403 def indent(s, depth): 404 """ 405 Indent a string of text with *depth* additional spaces on each line. 406 """ 407 spaces = " "*depth 408 sep = "\n" + spaces 409 return spaces + sep.join(s.split("\n")) 410 411 412 LOOP_OPEN = """\ 413 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 414 const double %(name)s = loops[2*(%(name)s_i%(offset)s)]; 415 const double %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 416 """ 417 def build_polydispersity_loops(pd_pars): 418 """ 419 Build polydispersity loops 420 421 Returns loop opening and loop closing 422 """ 423 depth = 4 424 offset = "" 425 loop_head = [] 426 loop_end = [] 427 for name in pd_pars: 428 subst = {'name': name, 'offset': offset} 429 loop_head.append(indent(LOOP_OPEN % subst, depth)) 430 loop_end.insert(0, (" "*depth) + "}") 431 offset += '+N' + name 432 depth += 2 433 return "\n".join(loop_head), "\n".join(loop_end) 434 435 C_KERNEL_TEMPLATE = None 436 def make_source(model_info): 437 """ 438 Generate the OpenCL/ctypes kernel from the module info. 439 440 Uses source files found in the given search path. 441 """ 442 if callable(model_info['Iq']): 443 return None 444 445 # TODO: need something other than volume to indicate dispersion parameters 446 # No volume normalization despite having a volume parameter. 447 # Thickness is labelled a volume in order to trigger polydispersity. 448 # May want a separate dispersion flag, or perhaps a separate category for 449 # disperse, but not volume. Volume parameters also use relative values 450 # for the distribution rather than the absolute values used by angular 451 # dispersion. Need to be careful that necessary parameters are available 452 # for computing volume even if we allow non-disperse volume parameters. 453 454 # Load template 455 global C_KERNEL_TEMPLATE 456 if C_KERNEL_TEMPLATE is None: 457 with open(C_KERNEL_TEMPLATE_PATH) as fid: 458 C_KERNEL_TEMPLATE = fid.read() 459 460 # Load additional sources 461 source = [open(f).read() for f in model_sources(model_info)] 462 463 # Prepare defines 464 defines = [] 465 partype = model_info['partype'] 466 pd_1d = partype['pd-1d'] 467 pd_2d = partype['pd-2d'] 468 fixed_1d = partype['fixed-1d'] 469 fixed_2d = partype['fixed-1d'] 470 471 iq_parameters = [p[0] 472 for p in model_info['parameters'][2:] # skip scale, background 473 if p[0] in set(fixed_1d + pd_1d)] 474 iqxy_parameters = [p[0] 475 for p in model_info['parameters'][2:] # skip scale, background 476 if p[0] in set(fixed_2d + pd_2d)] 477 volume_parameters = [p[0] 478 for p in model_info['parameters'] 479 if p[4] == 'volume'] 480 481 # Fill in defintions for volume parameters 482 if volume_parameters: 483 defines.append(('VOLUME_PARAMETERS', 484 ','.join(volume_parameters))) 485 defines.append(('VOLUME_WEIGHT_PRODUCT', 486 '*'.join(p + '_w' for p in volume_parameters))) 487 488 # Generate form_volume function from body only 489 if model_info['form_volume'] is not None: 490 if volume_parameters: 491 vol_par_decl = ', '.join('double ' + p for p in volume_parameters) 492 else: 493 vol_par_decl = 'void' 494 defines.append(('VOLUME_PARAMETER_DECLARATIONS', 495 vol_par_decl)) 496 fn = """\ 497 double form_volume(VOLUME_PARAMETER_DECLARATIONS); 498 double form_volume(VOLUME_PARAMETER_DECLARATIONS) { 499 %(body)s 500 } 501 """ % {'body':model_info['form_volume']} 502 source.append(fn) 503 504 # Fill in definitions for Iq parameters 505 defines.append(('IQ_KERNEL_NAME', model_info['name'] + '_Iq')) 506 defines.append(('IQ_PARAMETERS', ', '.join(iq_parameters))) 507 if fixed_1d: 508 defines.append(('IQ_FIXED_PARAMETER_DECLARATIONS', 509 ', \\\n '.join('const double %s' % p for p in fixed_1d))) 510 if pd_1d: 511 defines.append(('IQ_WEIGHT_PRODUCT', 512 '*'.join(p + '_w' for p in pd_1d))) 513 defines.append(('IQ_DISPERSION_LENGTH_DECLARATIONS', 514 ', \\\n '.join('const int N%s' % p for p in pd_1d))) 515 defines.append(('IQ_DISPERSION_LENGTH_SUM', 516 '+'.join('N' + p for p in pd_1d))) 517 open_loops, close_loops = build_polydispersity_loops(pd_1d) 518 defines.append(('IQ_OPEN_LOOPS', 519 open_loops.replace('\n', ' \\\n'))) 520 defines.append(('IQ_CLOSE_LOOPS', 521 close_loops.replace('\n', ' \\\n'))) 522 if model_info['Iq'] is not None: 523 defines.append(('IQ_PARAMETER_DECLARATIONS', 524 ', '.join('double ' + p for p in iq_parameters))) 525 fn = """\ 526 double Iq(double q, IQ_PARAMETER_DECLARATIONS); 527 double Iq(double q, IQ_PARAMETER_DECLARATIONS) { 528 %(body)s 529 } 530 """ % {'body':model_info['Iq']} 531 source.append(fn) 532 533 # Fill in definitions for Iqxy parameters 534 defines.append(('IQXY_KERNEL_NAME', model_info['name'] + '_Iqxy')) 535 defines.append(('IQXY_PARAMETERS', ', '.join(iqxy_parameters))) 536 if fixed_2d: 537 defines.append(('IQXY_FIXED_PARAMETER_DECLARATIONS', 538 ', \\\n '.join('const double %s' % p for p in fixed_2d))) 539 if pd_2d: 540 defines.append(('IQXY_WEIGHT_PRODUCT', 541 '*'.join(p + '_w' for p in pd_2d))) 542 defines.append(('IQXY_DISPERSION_LENGTH_DECLARATIONS', 543 ', \\\n '.join('const int N%s' % p for p in pd_2d))) 544 defines.append(('IQXY_DISPERSION_LENGTH_SUM', 545 '+'.join('N' + p for p in pd_2d))) 546 open_loops, close_loops = build_polydispersity_loops(pd_2d) 547 defines.append(('IQXY_OPEN_LOOPS', 548 open_loops.replace('\n', ' \\\n'))) 549 defines.append(('IQXY_CLOSE_LOOPS', 550 close_loops.replace('\n', ' \\\n'))) 551 if model_info['Iqxy'] is not None: 552 defines.append(('IQXY_PARAMETER_DECLARATIONS', 553 ', '.join('double ' + p for p in iqxy_parameters))) 554 fn = """\ 555 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS); 556 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS) { 557 %(body)s 558 } 559 """ % {'body':model_info['Iqxy']} 560 source.append(fn) 561 562 # Need to know if we have a theta parameter for Iqxy; it is not there 563 # for the magnetic sphere model, for example, which has a magnetic 564 # orientation but no shape orientation. 565 if 'theta' in pd_2d: 566 defines.append(('IQXY_HAS_THETA', '1')) 567 568 #for d in defines: print(d) 569 defines = '\n'.join('#define %s %s' % (k, v) for k, v in defines) 570 sources = '\n\n'.join(source) 571 return C_KERNEL_TEMPLATE % { 572 'DEFINES': defines, 573 'SOURCES': sources, 574 } 399 575 400 576 def categorize_parameters(pars): … … 403 579 404 580 Returns a dictionary of categories. 581 582 Note: these categories are subject to change, depending on the needs of 583 the UI and the needs of the kernel calling function. 584 585 The categories are as follows: 586 587 * *volume* list of volume parameter names 588 * *orientation* list of orientation parameters 589 * *magnetic* list of magnetic parameters 590 * *<empty string>* list of parameters that have no type info 591 592 Each parameter is in one and only one category. 593 594 The following derived categories are created: 595 596 * *fixed-1d* list of non-polydisperse parameters for 1D models 597 * *pd-1d* list of polydisperse parameters for 1D models 598 * *fixed-2d* list of non-polydisperse parameters for 2D models 599 * *pd-d2* list of polydisperse parameters for 2D models 405 600 """ 406 601 partype = { … … 429 624 return partype 430 625 431 def indent(s, depth): 432 """ 433 Indent a string of text with *depth* additional spaces on each line. 434 """ 435 spaces = " "*depth 436 sep = "\n" + spaces 437 return spaces + sep.join(s.split("\n")) 438 439 440 LOOP_OPEN = """\ 441 for (int %(name)s_i=0; %(name)s_i < N%(name)s; %(name)s_i++) { 442 const double %(name)s = loops[2*(%(name)s_i%(offset)s)]; 443 const double %(name)s_w = loops[2*(%(name)s_i%(offset)s)+1];\ 444 """ 445 def build_polydispersity_loops(pd_pars): 446 """ 447 Build polydispersity loops 448 449 Returns loop opening and loop closing 450 """ 451 depth = 4 452 offset = "" 453 loop_head = [] 454 loop_end = [] 455 for name in pd_pars: 456 subst = {'name': name, 'offset': offset} 457 loop_head.append(indent(LOOP_OPEN % subst, depth)) 458 loop_end.insert(0, (" "*depth) + "}") 459 offset += '+N' + name 460 depth += 2 461 return "\n".join(loop_head), "\n".join(loop_end) 462 463 C_KERNEL_TEMPLATE = None 464 def make_model(info): 465 """ 466 Generate the code for the kernel defined by info, using source files 467 found in the given search path. 468 """ 469 # TODO: need something other than volume to indicate dispersion parameters 470 # No volume normalization despite having a volume parameter. 471 # Thickness is labelled a volume in order to trigger polydispersity. 472 # May want a separate dispersion flag, or perhaps a separate category for 473 # disperse, but not volume. Volume parameters also use relative values 474 # for the distribution rather than the absolute values used by angular 475 # dispersion. Need to be careful that necessary parameters are available 476 # for computing volume even if we allow non-disperse volume parameters. 477 478 # Load template 479 global C_KERNEL_TEMPLATE 480 if C_KERNEL_TEMPLATE is None: 481 with open(C_KERNEL_TEMPLATE_PATH) as fid: 482 C_KERNEL_TEMPLATE = fid.read() 483 484 # Load additional sources 485 source = [open(f).read() for f in model_sources(info)] 486 487 # Prepare defines 488 defines = [] 489 partype = info['partype'] 490 pd_1d = partype['pd-1d'] 491 pd_2d = partype['pd-2d'] 492 fixed_1d = partype['fixed-1d'] 493 fixed_2d = partype['fixed-1d'] 494 495 iq_parameters = [p[0] 496 for p in info['parameters'][2:] # skip scale, background 497 if p[0] in set(fixed_1d + pd_1d)] 498 iqxy_parameters = [p[0] 499 for p in info['parameters'][2:] # skip scale, background 500 if p[0] in set(fixed_2d + pd_2d)] 501 volume_parameters = [p[0] 502 for p in info['parameters'] 503 if p[4] == 'volume'] 504 505 # Fill in defintions for volume parameters 506 if volume_parameters: 507 defines.append(('VOLUME_PARAMETERS', 508 ','.join(volume_parameters))) 509 defines.append(('VOLUME_WEIGHT_PRODUCT', 510 '*'.join(p + '_w' for p in volume_parameters))) 511 512 # Generate form_volume function from body only 513 if info['form_volume'] is not None: 514 if volume_parameters: 515 vol_par_decl = ', '.join('double ' + p for p in volume_parameters) 516 else: 517 vol_par_decl = 'void' 518 defines.append(('VOLUME_PARAMETER_DECLARATIONS', 519 vol_par_decl)) 520 fn = """\ 521 double form_volume(VOLUME_PARAMETER_DECLARATIONS); 522 double form_volume(VOLUME_PARAMETER_DECLARATIONS) { 523 %(body)s 524 } 525 """ % {'body':info['form_volume']} 526 source.append(fn) 527 528 # Fill in definitions for Iq parameters 529 defines.append(('IQ_KERNEL_NAME', info['name'] + '_Iq')) 530 defines.append(('IQ_PARAMETERS', ', '.join(iq_parameters))) 531 if fixed_1d: 532 defines.append(('IQ_FIXED_PARAMETER_DECLARATIONS', 533 ', \\\n '.join('const double %s' % p for p in fixed_1d))) 534 if pd_1d: 535 defines.append(('IQ_WEIGHT_PRODUCT', 536 '*'.join(p + '_w' for p in pd_1d))) 537 defines.append(('IQ_DISPERSION_LENGTH_DECLARATIONS', 538 ', \\\n '.join('const int N%s' % p for p in pd_1d))) 539 defines.append(('IQ_DISPERSION_LENGTH_SUM', 540 '+'.join('N' + p for p in pd_1d))) 541 open_loops, close_loops = build_polydispersity_loops(pd_1d) 542 defines.append(('IQ_OPEN_LOOPS', 543 open_loops.replace('\n', ' \\\n'))) 544 defines.append(('IQ_CLOSE_LOOPS', 545 close_loops.replace('\n', ' \\\n'))) 546 if info['Iq'] is not None: 547 defines.append(('IQ_PARAMETER_DECLARATIONS', 548 ', '.join('double ' + p for p in iq_parameters))) 549 fn = """\ 550 double Iq(double q, IQ_PARAMETER_DECLARATIONS); 551 double Iq(double q, IQ_PARAMETER_DECLARATIONS) { 552 %(body)s 553 } 554 """ % {'body':info['Iq']} 555 source.append(fn) 556 557 # Fill in definitions for Iqxy parameters 558 defines.append(('IQXY_KERNEL_NAME', info['name'] + '_Iqxy')) 559 defines.append(('IQXY_PARAMETERS', ', '.join(iqxy_parameters))) 560 if fixed_2d: 561 defines.append(('IQXY_FIXED_PARAMETER_DECLARATIONS', 562 ', \\\n '.join('const double %s' % p for p in fixed_2d))) 563 if pd_2d: 564 defines.append(('IQXY_WEIGHT_PRODUCT', 565 '*'.join(p + '_w' for p in pd_2d))) 566 defines.append(('IQXY_DISPERSION_LENGTH_DECLARATIONS', 567 ', \\\n '.join('const int N%s' % p for p in pd_2d))) 568 defines.append(('IQXY_DISPERSION_LENGTH_SUM', 569 '+'.join('N' + p for p in pd_2d))) 570 open_loops, close_loops = build_polydispersity_loops(pd_2d) 571 defines.append(('IQXY_OPEN_LOOPS', 572 open_loops.replace('\n', ' \\\n'))) 573 defines.append(('IQXY_CLOSE_LOOPS', 574 close_loops.replace('\n', ' \\\n'))) 575 if info['Iqxy'] is not None: 576 defines.append(('IQXY_PARAMETER_DECLARATIONS', 577 ', '.join('double ' + p for p in iqxy_parameters))) 578 fn = """\ 579 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS); 580 double Iqxy(double qx, double qy, IQXY_PARAMETER_DECLARATIONS) { 581 %(body)s 582 } 583 """ % {'body':info['Iqxy']} 584 source.append(fn) 585 586 # Need to know if we have a theta parameter for Iqxy; it is not there 587 # for the magnetic sphere model, for example, which has a magnetic 588 # orientation but no shape orientation. 589 if 'theta' in pd_2d: 590 defines.append(('IQXY_HAS_THETA', '1')) 591 592 #for d in defines: print(d) 593 defines = '\n'.join('#define %s %s' % (k, v) for k, v in defines) 594 sources = '\n\n'.join(source) 595 return C_KERNEL_TEMPLATE % { 596 'DEFINES': defines, 597 'SOURCES': sources, 598 } 599 600 def make_info(kernel_module): 626 def process_parameters(model_info): 627 """ 628 Process parameter block, precalculating parameter details. 629 """ 630 # Fill in the derived attributes 631 model_info['limits'] = dict((p[0], p[3]) for p in model_info['parameters']) 632 model_info['partype'] = categorize_parameters(model_info['parameters']) 633 model_info['defaults'] = dict((p[0], p[2]) for p in model_info['parameters']) 634 if model_info.get('demo', None) is None: 635 model_info['demo'] = model_info['defaults'] 636 637 def make_model_info(kernel_module): 601 638 """ 602 639 Interpret the model definition file, categorizing the parameters. 603 """ 640 641 The module can be loaded with a normal python import statement if you 642 know which module you need, or with __import__('sasmodels.model.'+name) 643 if the name is in a string. 644 645 The *model_info* structure contains the following fields: 646 647 * *id* is the id of the kernel 648 * *name* is the display name of the kernel 649 * *title* is a short description of the kernel 650 * *description* is a long description of the kernel (this doesn't seem 651 very useful since the Help button on the model page brings you directly 652 to the documentation page) 653 * *docs* is the docstring from the module. Use :func:`make_doc` to 654 * *category* specifies the model location in the docs 655 * *parameters* is the model parameter table 656 * *single* is True if the model allows single precision 657 * *defaults* is the *{parameter: value}* table built from the parameter 658 description table. 659 * *limits* is the *{parameter: [min, max]}* table built from the 660 parameter description table. 661 * *partypes* categorizes the model parameters. See 662 :func:`categorize_parameters` for details. 663 * *demo* contains the *{parameter: value}* map used in compare (and maybe 664 for the demo plot, if plots aren't set up to use the default values). 665 If *demo* is not given in the file, then the default values will be used. 666 * *tests* is a set of tests that must pass 667 * *source* is the list of library files to include in the C model build 668 * *Iq*, *Iqxy*, *form_volume*, *ER*, and *VR* are python functions 669 implementing the kernel for the module, or None if they are not 670 defined in python 671 * *oldname* is the model name in pre-4.0 Sasview 672 * *oldpars* is the *{new: old}* parameter translation table 673 from pre-4.0 Sasview 674 * *composition* is None if the model is independent, otherwise it is a 675 tuple with composition type ('product' or 'mixture') and a list of 676 *model_info* blocks for the composition objects. This allows us to 677 build complete product and mixture models from just the info. 678 """ 679 # TODO: maybe turn model_info into a class ModelDefinition 604 680 #print(kernelfile) 605 681 category = getattr(kernel_module, 'category', None) … … 608 684 # parameters if an explicit demo parameter set has not been specified. 609 685 demo_parameters = getattr(kernel_module, 'demo', None) 610 if demo_parameters is None:611 demo_parameters = dict((p[0], p[2]) for p in parameters)612 686 filename = abspath(kernel_module.__file__) 613 687 kernel_id = splitext(basename(filename))[0] 614 688 name = getattr(kernel_module, 'name', None) 689 single = getattr(kernel_module, 'single', True) 615 690 if name is None: 616 691 name = " ".join(w.capitalize() for w in kernel_id.split('_')) 617 info = dict(692 model_info = dict( 618 693 id=kernel_id, # string used to load the kernel 619 694 filename=abspath(kernel_module.__file__), … … 621 696 title=kernel_module.title, 622 697 description=kernel_module.description, 698 docs=kernel_module.__doc__, 623 699 category=category, 624 700 parameters=parameters, 701 composition=None, 702 single=single, 625 703 demo=demo_parameters, 626 704 source=getattr(kernel_module, 'source', []), 627 oldname=kernel_module.oldname, 628 oldpars=kernel_module.oldpars, 705 oldname=getattr(kernel_module, 'oldname', None), 706 oldpars=getattr(kernel_module, 'oldpars', {}), 707 tests=getattr(kernel_module, 'tests', []), 629 708 ) 709 process_parameters(model_info) 630 710 # Fill in attributes which default to None 631 info.update((k, getattr(kernel_module, k, None)) 632 for k in ('ER', 'VR', 'form_volume', 'Iq', 'Iqxy')) 633 # Fill in the derived attributes 634 info['limits'] = dict((p[0], p[3]) for p in info['parameters']) 635 info['partype'] = categorize_parameters(info['parameters']) 636 info['defaults'] = dict((p[0], p[2]) for p in info['parameters']) 637 return info 638 639 def make(kernel_module): 640 """ 641 Build an OpenCL/ctypes function from the definition in *kernel_module*. 642 643 The module can be loaded with a normal python import statement if you 644 know which module you need, or with __import__('sasmodels.model.'+name) 645 if the name is in a string. 646 """ 647 info = make_info(kernel_module) 648 # Assume if one part of the kernel is python then all parts are. 649 source = make_model(info) if not callable(info['Iq']) else None 650 return source, info 711 model_info.update((k, getattr(kernel_module, k, None)) 712 for k in ('ER', 'VR', 'form_volume', 'Iq', 'Iqxy')) 713 return model_info 651 714 652 715 section_marker = re.compile(r'\A(?P<first>[%s])(?P=first)*\Z' … … 683 746 return "\n".join(_convert_section_titles_to_boldface(s.split('\n'))) 684 747 685 def doc(kernel_module):748 def make_doc(model_info): 686 749 """ 687 750 Return the documentation for the model. … … 689 752 Iq_units = "The returned value is scaled to units of |cm^-1| |sr^-1|, absolute scale." 690 753 Sq_units = "The returned value is a dimensionless structure factor, $S(q)$." 691 info = make_info(kernel_module) 692 is_Sq = ("structure-factor" in info['category']) 754 is_Sq = ("structure-factor" in model_info['category']) 693 755 #docs = kernel_module.__doc__ 694 docs = convert_section_titles_to_boldface( kernel_module.__doc__)695 subst = dict(id= info['id'].replace('_', '-'),696 name= info['name'],697 title= info['title'],698 parameters=make_partable( info['parameters']),756 docs = convert_section_titles_to_boldface(model_info['docs']) 757 subst = dict(id=model_info['id'].replace('_', '-'), 758 name=model_info['name'], 759 title=model_info['title'], 760 parameters=make_partable(model_info['parameters']), 699 761 returns=Sq_units if is_Sq else Iq_units, 700 762 docs=docs) … … 710 772 import datetime 711 773 tic = datetime.datetime.now() 712 make (cylinder)774 make_source(make_model_info(cylinder)) 713 775 toc = (datetime.datetime.now() - tic).total_seconds() 714 776 print("time: %g"%toc) … … 725 787 __import__('sasmodels.models.' + name) 726 788 model = getattr(sasmodels.models, name) 727 source, _ = make(model) 789 model_info = make_model_info(model) 790 source = make_source(model_info) 728 791 print(source) 729 792
Note: See TracChangeset
for help on using the changeset viewer.