Changeset ad79f49 in sasmodels
- Timestamp:
- Sep 12, 2017 11:29:02 AM (7 years ago)
- Branches:
- master, core_shell_microgels, costrafo411, magnetic_model, ticket-1257-vesicle-product, ticket_1156, ticket_1265_superball, ticket_822_more_unit_tests
- Children:
- 946c8d27
- Parents:
- a57a902 (diff), b18e650 (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. - Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
.travis.yml
r947a61e rce8c388 24 24 packages: opencl-headers 25 25 before_install: 26 - openssl aes-256-cbc -K $encrypted_fe6026add10a_key -iv $encrypted_fe6026add10a_iv27 -in .travis/travis_rsa.enc -out .travis/travis_rsa -d 26 - if [[ $encrypted_cb04388797b6_iv ]]; then openssl aes-256-cbc -K $encrypted_cb04388797b6_key -iv $encrypted_cb04388797b6_iv 27 -in .travis/travis_rsa.enc -out .travis/travis_rsa -d; fi; 28 28 - echo $TRAVIS_OS_NAME 29 29 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh … … 46 46 - echo -e "Host danse.chem.utk.edu\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config 47 47 deploy: 48 49 50 script: /bin/sh -ex ./deploy.sh51 52 48 skip_cleanup: true 49 provider: script 50 script: "/bin/sh -ex ./deploy.sh" 51 on: 52 branch: master 53 53 notifications: 54 54 slack: -
deploy.sh
rd9d77b0 r9d1e3e4 1 eval "$(ssh-agent -s)" 2 chmod 600 .travis/travis_rsa 3 ssh-add .travis/travis_rsa 4 git remote add deploy git@danse.chem.utk.edu:/home/git/sasmodels 5 git push deploy master 1 if [[ $encrypted_cb04388797b6_iv ]] 2 then 3 eval "$(ssh-agent -s)" 4 chmod 600 .travis/travis_rsa 5 ssh-add .travis/travis_rsa 6 git remote add deploy git@danse.chem.utk.edu:/home/git/sasmodels 7 git push deploy master 8 fi -
sasmodels/compare.py
rd9ec8f9 r765eb0e 88 88 -magnetic/-nonmagnetic* suppress magnetism 89 89 -accuracy=Low accuracy of the resolution calculation Low, Mid, High, Xhigh 90 -neval=1 sets the number of evals for more accurate timing 90 91 91 92 === precision options === … … 1013 1014 'magnetic', 'nonmagnetic', 1014 1015 'accuracy=', 1016 'neval=', # for timing... 1015 1017 1016 1018 # Precision options … … 1018 1020 'half', 'fast', 'single', 'double', 'single!', 'double!', 'quad!', 1019 1021 'sasview', # TODO: remove sasview 3.x support 1020 'timing=',1021 1022 1022 1023 # Output options … … 1068 1069 1069 1070 # Plug in values given in demo 1070 if use_demo :1071 if use_demo and model_info.demo: 1071 1072 pars.update(model_info.demo) 1072 1073 return pars -
sasmodels/core.py
r142a8e2 ra85a569 10 10 11 11 import os 12 import re 12 13 from os.path import basename, dirname, join as joinpath 13 14 from glob import glob … … 21 22 from . import kernelpy 22 23 from . import kerneldll 24 from . import custom 23 25 24 26 if os.environ.get("SAS_OPENCL", "").lower() == "none": … … 30 32 except Exception: 31 33 HAVE_OPENCL = False 34 35 CUSTOM_MODEL_PATH = os.environ.get('SAS_MODELPATH', "") 36 if CUSTOM_MODEL_PATH == "": 37 path = joinpath(os.path.expanduser("~"), ".sasmodels", "custom_models") 38 if not os.path.isdir(path): 39 os.makedirs(path) 40 CUSTOM_MODEL_PATH = path 32 41 33 42 try: … … 125 134 dtype=dtype, platform=platform) 126 135 127 128 def load_model_info(model_name): 136 def load_model_info(model_string): 129 137 # type: (str) -> modelinfo.ModelInfo 130 138 """ 131 139 Load a model definition given the model name. 132 140 133 *model_name* is the name of the model, or perhaps a model expression 134 such as sphere*hardsphere or sphere+cylinder. 141 *model_string* is the name of the model, or perhaps a model expression 142 such as sphere*cylinder or sphere+cylinder. Use '@' for a structure 143 factor product, e.g. sphere@hardsphere. Custom models can be specified by 144 prefixing the model name with 'custom.', e.g. 'custom.MyModel+sphere'. 135 145 136 146 This returns a handle to the module defining the model. This can be 137 147 used with functions in generate to build the docs or extract model info. 138 148 """ 139 parts = model_name.split('+') 140 if len(parts) > 1: 141 model_info_list = [load_model_info(p) for p in parts] 142 return mixture.make_mixture_info(model_info_list) 143 144 parts = model_name.split('*') 145 if len(parts) > 1: 146 if len(parts) > 2: 147 raise ValueError("use P*S to apply structure factor S to model P") 148 P_info, Q_info = [load_model_info(p) for p in parts] 149 if '@' in model_string: 150 parts = model_string.split('@') 151 if len(parts) != 2: 152 raise ValueError("Use P@S to apply a structure factor S to model P") 153 P_info, Q_info = [load_model_info(part) for part in parts] 149 154 return product.make_product_info(P_info, Q_info) 150 155 151 kernel_module = generate.load_kernel_module(model_name) 152 return modelinfo.make_model_info(kernel_module) 156 product_parts = [] 157 addition_parts = [] 158 159 addition_parts_names = model_string.split('+') 160 if len(addition_parts_names) >= 2: 161 addition_parts = [load_model_info(part) for part in addition_parts_names] 162 elif len(addition_parts_names) == 1: 163 product_parts_names = model_string.split('*') 164 if len(product_parts_names) >= 2: 165 product_parts = [load_model_info(part) for part in product_parts_names] 166 elif len(product_parts_names) == 1: 167 if "custom." in product_parts_names[0]: 168 # Extract ModelName from "custom.ModelName" 169 pattern = "custom.([A-Za-z0-9_-]+)" 170 result = re.match(pattern, product_parts_names[0]) 171 if result is None: 172 raise ValueError("Model name in invalid format: " + product_parts_names[0]) 173 model_name = result.group(1) 174 # Use ModelName to find the path to the custom model file 175 model_path = joinpath(CUSTOM_MODEL_PATH, model_name + ".py") 176 if not os.path.isfile(model_path): 177 raise ValueError("The model file {} doesn't exist".format(model_path)) 178 kernel_module = custom.load_custom_kernel_module(model_path) 179 return modelinfo.make_model_info(kernel_module) 180 # Model is a core model 181 kernel_module = generate.load_kernel_module(product_parts_names[0]) 182 return modelinfo.make_model_info(kernel_module) 183 184 model = None 185 if len(product_parts) > 1: 186 model = mixture.make_mixture_info(product_parts, operation='*') 187 if len(addition_parts) > 1: 188 if model is not None: 189 addition_parts.append(model) 190 model = mixture.make_mixture_info(addition_parts, operation='+') 191 return model 153 192 154 193 -
sasmodels/mixture.py
r6dc78e4 r0edb6c1 25 25 pass 26 26 27 def make_mixture_info(parts ):27 def make_mixture_info(parts, operation='+'): 28 28 # type: (List[ModelInfo]) -> ModelInfo 29 29 """ 30 30 Create info block for mixture model. 31 31 """ 32 flatten = []33 for part in parts:34 if part.composition and part.composition[0] == 'mixture':35 flatten.extend(part.composition[1])36 else:37 flatten.append(part)38 parts = flatten39 40 32 # Build new parameter list 41 33 combined_pars = [] 42 demo = {} 43 for k, part in enumerate(parts): 34 35 model_num = 0 36 all_parts = copy(parts) 37 is_flat = False 38 while not is_flat: 39 is_flat = True 40 for part in all_parts: 41 if part.composition and part.composition[0] == 'mixture' and \ 42 len(part.composition[1]) > 1: 43 all_parts += part.composition[1] 44 all_parts.remove(part) 45 is_flat = False 46 47 # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 48 # the parameters for models 1 & 2 will be prefixed with A & B respectively, 49 # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 50 # so that they are prefixed with C & D to avoid overlap of parameter names. 51 used_prefixes = [] 52 for part in parts: 53 i = 0 54 if part.composition and part.composition[0] == 'mixture': 55 npars_list = [info.parameters.npars for info in part.composition[1]] 56 for npars in npars_list: 57 # List of params of one of the constituent models of part 58 submodel_pars = part.parameters.kernel_parameters[i:i+npars] 59 # Prefix of the constituent model 60 prefix = submodel_pars[0].name[0] 61 if prefix not in used_prefixes: # Haven't seen this prefix so far 62 used_prefixes.append(prefix) 63 i += npars 64 continue 65 while prefix in used_prefixes: 66 # This prefix has been already used, so change it to the 67 # next letter that hasn't been used 68 prefix = chr(ord(prefix) + 1) 69 used_prefixes.append(prefix) 70 prefix += "_" 71 # Update the parameters of this constituent model to use the 72 # new prefix 73 for par in submodel_pars: 74 par.id = prefix + par.id[2:] 75 par.name = prefix + par.name[2:] 76 if par.length_control is not None: 77 par.length_control = prefix + par.length_control[2:] 78 i += npars 79 80 for part in parts: 44 81 # Parameter prefix per model, A_, B_, ... 45 82 # Note that prefix must also be applied to id and length_control 46 83 # to support vector parameters 47 prefix = chr(ord('A')+k) + '_' 48 scale = Parameter(prefix+'scale', default=1.0, 49 description="model intensity for " + part.name) 50 combined_pars.append(scale) 84 prefix = '' 85 if not part.composition: 86 # Model isn't a composition model, so it's parameters don't have a 87 # a prefix. Add the next available prefix 88 prefix = chr(ord('A')+len(used_prefixes)) 89 used_prefixes.append(prefix) 90 prefix += '_' 91 92 if operation == '+': 93 # If model is a sum model, each constituent model gets its own scale parameter 94 scale_prefix = prefix 95 if prefix == '' and part.operation == '*': 96 # `part` is a composition product model. Find the prefixes of 97 # it's parameters to form a new prefix for the scale, eg: 98 # a model with A*B*C will have ABC_scale 99 sub_prefixes = [] 100 for param in part.parameters.kernel_parameters: 101 # Prefix of constituent model 102 sub_prefix = param.id.split('_')[0] 103 if sub_prefix not in sub_prefixes: 104 sub_prefixes.append(sub_prefix) 105 # Concatenate sub_prefixes to form prefix for the scale 106 scale_prefix = ''.join(sub_prefixes) + '_' 107 scale = Parameter(scale_prefix + 'scale', default=1.0, 108 description="model intensity for " + part.name) 109 combined_pars.append(scale) 51 110 for p in part.parameters.kernel_parameters: 52 111 p = copy(p) … … 56 115 p.length_control = prefix + p.length_control 57 116 combined_pars.append(p) 58 demo.update((prefix+k, v) for k, v in part.demo.items()59 if k != "background")60 #print("pars",combined_pars)61 117 parameters = ParameterTable(combined_pars) 62 118 parameters.max_pd = sum(part.parameters.max_pd for part in parts) 63 119 120 def random(): 121 combined_pars = {} 122 for k, part in enumerate(parts): 123 prefix = chr(ord('A')+k) + '_' 124 pars = part.random() 125 combined_pars.update((prefix+k, v) for k, v in pars.items()) 126 return combined_pars 127 64 128 model_info = ModelInfo() 65 model_info.id = '+'.join(part.id for part in parts) 66 model_info.name = ' + '.join(part.name for part in parts) 129 model_info.id = operation.join(part.id for part in parts) 130 model_info.operation = operation 131 model_info.name = '(' + operation.join(part.name for part in parts) + ')' 67 132 model_info.filename = None 68 133 model_info.title = 'Mixture model with ' + model_info.name … … 71 136 model_info.category = "custom" 72 137 model_info.parameters = parameters 138 model_info.random = random 73 139 #model_info.single = any(part['single'] for part in parts) 74 140 model_info.structure_factor = False … … 79 145 # Remember the component info blocks so we can build the model 80 146 model_info.composition = ('mixture', parts) 81 model_info.demo = demo82 147 return model_info 83 148 … … 88 153 self.info = model_info 89 154 self.parts = parts 155 self.dtype = parts[0].dtype 90 156 91 157 def make_kernel(self, q_vectors): … … 116 182 self.kernels = kernels 117 183 self.dtype = self.kernels[0].dtype 184 self.operation = model_info.operation 118 185 self.results = [] # type: List[np.ndarray] 119 186 … … 124 191 # remember the parts for plotting later 125 192 self.results = [] # type: List[np.ndarray] 126 offset = 2 # skip scale & background127 193 parts = MixtureParts(self.info, self.kernels, call_details, values) 128 194 for kernel, kernel_details, kernel_values in parts: 129 195 #print("calling kernel", kernel.info.name) 130 196 result = kernel(kernel_details, kernel_values, cutoff, magnetic) 131 #print(kernel.info.name, result) 132 total += result 197 result = np.array(result).astype(kernel.dtype) 198 # print(kernel.info.name, result) 199 if self.operation == '+': 200 total += result 201 elif self.operation == '*': 202 if np.all(total) == 0.0: 203 total = result 204 else: 205 total *= result 133 206 self.results.append(result) 134 207 … … 171 244 172 245 self.part_num += 1 173 self.par_index += info.parameters.npars + 1 246 self.par_index += info.parameters.npars 247 if self.model_info.operation == '+': 248 self.par_index += 1 # Account for each constituent model's scale param 174 249 self.mag_index += 3 * len(info.parameters.magnetism_index) 175 250 … … 182 257 # which includes the initial scale and background parameters. 183 258 # We want the index into the weight length/offset for each parameter. 184 # Exclude the initial scale and background, so subtract two, but each 185 # component has its own scale factor which we need to skip when 186 # constructing the details for the kernel, so add one, giving a 187 # net subtract one. 188 index = slice(par_index - 1, par_index - 1 + info.parameters.npars) 259 # Exclude the initial scale and background, so subtract two. If we're 260 # building an addition model, each component has its own scale factor 261 # which we need to skip when constructing the details for the kernel, so 262 # add one, giving a net subtract one. 263 diff = 1 if self.model_info.operation == '+' else 2 264 index = slice(par_index - diff, par_index - diff + info.parameters.npars) 189 265 length = full.length[index] 190 266 offset = full.offset[index] … … 196 272 def _part_values(self, info, par_index, mag_index): 197 273 # type: (ModelInfo, int, int) -> np.ndarray 198 #print(info.name, par_index, self.values[par_index:par_index + info.parameters.npars + 1]) 199 scale = self.values[par_index] 200 pars = self.values[par_index + 1:par_index + info.parameters.npars + 1] 274 # Set each constituent model's scale to 1 if this is a multiplication model 275 scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 276 diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 277 pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 201 278 nmagnetic = len(info.parameters.magnetism_index) 202 279 if nmagnetic: -
sasmodels/model_test.py
rbedb9b0 r65314f7 201 201 ({}, 'VR', None), 202 202 ] 203 204 tests = smoke_tests + self.info.tests 203 tests = smoke_tests 204 if self.info.tests is not None: 205 tests += self.info.tests 205 206 try: 206 207 model = build_model(self.info, dtype=self.dtype, … … 371 372 stream.writeln(traceback.format_exc()) 372 373 return 373 374 374 # Run the test suite 375 375 suite.run(result) -
sasmodels/modelinfo.py
r0bdddc2 ra85a569 727 727 models when the model is first called, not when the model is loaded. 728 728 """ 729 if hasattr(kernel_module, "model_info"): 730 # Custom sum/multi models 731 return kernel_module.model_info 729 732 info = ModelInfo() 730 733 #print("make parameter table", kernel_module.parameters) -
sasmodels/models/spherical_sld.py
r63a7fe8 r2ad5d30 18 18 Interface shapes are as follows:: 19 19 20 0: erf( |nu|*z)21 1: Rpow( z^|nu|)22 2: Lpow( z^|nu|)23 3: Rexp( -|nu|z)24 4: Lexp( -|nu|z)20 0: erf($\nu z$) 21 1: Rpow($z^\nu$) 22 2: Lpow($z^\nu$) 23 3: Rexp($-\nu z$) 24 4: Lexp($-\nu z$) 25 25 26 26 Definition -
sasmodels/product.py
r8f04da4 r0edb6c1 68 68 translate_name = dict((old.id, new.id) for old, new 69 69 in zip(s_pars.kernel_parameters[1:], s_list)) 70 demo = {}71 demo.update(p_info.demo.items())72 demo.update((translate_name[k], v) for k, v in s_info.demo.items()73 if k not in ("background", "scale") and not k.startswith(ER_ID))74 70 combined_pars = p_pars.kernel_parameters + s_list 75 71 parameters = ParameterTable(combined_pars) 76 72 parameters.max_pd = p_pars.max_pd + s_pars.max_pd 73 def random(): 74 combined_pars = p_info.random() 75 s_names = set(par.id for par in s_pars.kernel_parameters[1:]) 76 s = s_info.random() 77 combined_pars.update((translate_name[k], v) 78 for k, v in s_info.random().items() 79 if k in s_names) 80 return combined_pars 77 81 78 82 model_info = ModelInfo() 79 model_info.id = ' *'.join((p_id, s_id))80 model_info.name = ' *'.join((p_name, s_name))83 model_info.id = '@'.join((p_id, s_id)) 84 model_info.name = '@'.join((p_name, s_name)) 81 85 model_info.filename = None 82 86 model_info.title = 'Product of %s and %s'%(p_name, s_name) … … 85 89 model_info.category = "custom" 86 90 model_info.parameters = parameters 91 model_info.random = random 87 92 #model_info.single = p_info.single and s_info.single 88 93 model_info.structure_factor = False … … 95 100 # TODO: delegate random to p_info, s_info 96 101 #model_info.random = lambda: {} 97 model_info.demo = demo 98 99 ## Show the parameter table with the demo values 102 103 ## Show the parameter table 100 104 #from .compare import get_pars, parlist 101 105 #print("==== %s ====="%model_info.name) 102 #values = get_pars(model_info , use_demo=True)106 #values = get_pars(model_info) 103 107 #print(parlist(model_info, values, is2d=True)) 104 108 return model_info … … 126 130 self.P = P 127 131 self.S = S 132 self.dtype = P.dtype 128 133 129 134 def make_kernel(self, q_vectors): -
sasmodels/sasview_model.py
r44ca3e1 rad79f49 120 120 else: 121 121 model_info = modelinfo.make_model_info(kernel_module) 122 model = _make_model_from_info(model_info)122 model = make_model_from_info(model_info) 123 123 model.timestamp = getmtime(path) 124 124 … … 142 142 143 143 144 def make_model_from_info(model_info): 145 # type: (ModelInfo) -> SasviewModelType 146 """ 147 Convert *model_info* into a SasView model wrapper. 148 """ 149 def __init__(self, multiplicity=None): 150 SasviewModel.__init__(self, multiplicity=multiplicity) 151 attrs = _generate_model_attributes(model_info) 152 attrs['__init__'] = __init__ 153 attrs['filename'] = model_info.filename 154 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 155 return ConstructedModel 156 157 144 158 def _make_standard_model(name): 145 159 # type: (str) -> SasviewModelType … … 153 167 kernel_module = generate.load_kernel_module(name) 154 168 model_info = modelinfo.make_model_info(kernel_module) 155 return _make_model_from_info(model_info)169 return make_model_from_info(model_info) 156 170 157 171 … … 187 201 model_info = product.make_product_info(form_factor._model_info, 188 202 structure_factor._model_info) 189 ConstructedModel = _make_model_from_info(model_info)203 ConstructedModel = make_model_from_info(model_info) 190 204 return ConstructedModel() 191 205 192 def _make_model_from_info(model_info):193 # type: (ModelInfo) -> SasviewModelType194 """195 Convert *model_info* into a SasView model wrapper.196 """197 def __init__(self, multiplicity=None):198 SasviewModel.__init__(self, multiplicity=multiplicity)199 attrs = _generate_model_attributes(model_info)200 attrs['__init__'] = __init__201 attrs['filename'] = model_info.filename202 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType203 return ConstructedModel204 206 205 207 def _generate_model_attributes(model_info):
Note: See TracChangeset
for help on using the changeset viewer.