Changes in / [9e9d96b:833c8d3] in sasmodels
- Location:
- sasmodels
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
sasmodels/core.py
r60335cc rbb39b4a 10 10 11 11 import os 12 import re13 12 from os.path import basename, dirname, join as joinpath 14 13 from glob import glob … … 22 21 from . import kernelpy 23 22 from . import kerneldll 24 from . import custom25 23 26 24 if os.environ.get("SAS_OPENCL", "").lower() == "none": … … 32 30 except Exception: 33 31 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 = path41 32 42 33 try: … … 134 125 dtype=dtype, platform=platform) 135 126 136 def load_model_info(model_string): 127 128 def load_model_info(model_name): 137 129 # type: (str) -> modelinfo.ModelInfo 138 130 """ 139 131 Load a model definition given the model name. 140 132 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'. 133 *model_name* is the name of the model, or perhaps a model expression 134 such as sphere*hardsphere or sphere+cylinder. 145 135 146 136 This returns a handle to the module defining the model. This can be 147 137 used with functions in generate to build the docs or extract model info. 148 138 """ 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] 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] 154 149 return product.make_product_info(P_info, Q_info) 155 150 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 151 kernel_module = generate.load_kernel_module(model_name) 152 return modelinfo.make_model_info(kernel_module) 192 153 193 154 -
sasmodels/mixture.py
r31ae428 r6dc78e4 25 25 pass 26 26 27 def make_mixture_info(parts , operation='+'):27 def make_mixture_info(parts): 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 = flatten 39 32 40 # Build new parameter list 33 41 combined_pars = [] 34 42 demo = {} 35 36 model_num = 0 37 all_parts = copy(parts) 38 is_flat = False 39 while not is_flat: 40 is_flat = True 41 for part in all_parts: 42 if part.composition and part.composition[0] == 'mixture' and \ 43 len(part.composition[1]) > 1: 44 all_parts += part.composition[1] 45 all_parts.remove(part) 46 is_flat = False 47 48 # When creating a mixture model that is a sum of product models (ie (1*2)+(3*4)) 49 # the parameters for models 1 & 2 will be prefixed with A & B respectively, 50 # but so will the parameters for models 3 & 4. We need to rename models 3 & 4 51 # so that they are prefixed with C & D to avoid overlap of parameter names. 52 used_prefixes = [] 53 for part in parts: 54 i = 0 55 if part.composition and part.composition[0] == 'mixture': 56 npars_list = [info.parameters.npars for info in part.composition[1]] 57 for npars in npars_list: 58 # List of params of one of the constituent models of part 59 submodel_pars = part.parameters.kernel_parameters[i:i+npars] 60 # Prefix of the constituent model 61 prefix = submodel_pars[0].name[0] 62 if prefix not in used_prefixes: # Haven't seen this prefix so far 63 used_prefixes.append(prefix) 64 i += npars 65 continue 66 while prefix in used_prefixes: 67 # This prefix has been already used, so change it to the 68 # next letter that hasn't been used 69 prefix = chr(ord(prefix) + 1) 70 used_prefixes.append(prefix) 71 prefix += "_" 72 # Update the parameters of this constituent model to use the 73 # new prefix 74 for par in submodel_pars: 75 par.id = prefix + par.id[2:] 76 par.name = prefix + par.name[2:] 77 if par.length_control is not None: 78 par.length_control = prefix + par.length_control[2:] 79 i += npars 80 81 for part in parts: 43 for k, part in enumerate(parts): 82 44 # Parameter prefix per model, A_, B_, ... 83 45 # Note that prefix must also be applied to id and length_control 84 46 # to support vector parameters 85 prefix = '' 86 if not part.composition: 87 # Model isn't a composition model, so it's parameters don't have a 88 # a prefix. Add the next available prefix 89 prefix = chr(ord('A')+len(used_prefixes)) 90 used_prefixes.append(prefix) 91 prefix += '_' 92 93 if operation == '+': 94 # If model is a sum model, each constituent model gets its own scale parameter 95 scale_prefix = prefix 96 if prefix == '' and part.operation == '*': 97 # `part` is a composition product model. Find the prefixes of 98 # it's parameters to form a new prefix for the scale, eg: 99 # a model with A*B*C will have ABC_scale 100 sub_prefixes = [] 101 for param in part.parameters.kernel_parameters: 102 # Prefix of constituent model 103 sub_prefix = param.id.split('_')[0] 104 if sub_prefix not in sub_prefixes: 105 sub_prefixes.append(sub_prefix) 106 # Concatenate sub_prefixes to form prefix for the scale 107 scale_prefix = ''.join(sub_prefixes) + '_' 108 scale = Parameter(scale_prefix + 'scale', default=1.0, 109 description="model intensity for " + part.name) 110 combined_pars.append(scale) 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) 111 51 for p in part.parameters.kernel_parameters: 112 52 p = copy(p) … … 123 63 124 64 model_info = ModelInfo() 125 model_info.id = operation.join(part.id for part in parts) 126 model_info.operation = operation 127 model_info.name = '(' + operation.join(part.name for part in parts) + ')' 65 model_info.id = '+'.join(part.id for part in parts) 66 model_info.name = ' + '.join(part.name for part in parts) 128 67 model_info.filename = None 129 68 model_info.title = 'Mixture model with ' + model_info.name … … 177 116 self.kernels = kernels 178 117 self.dtype = self.kernels[0].dtype 179 self.operation = model_info.operation180 118 self.results = [] # type: List[np.ndarray] 181 119 … … 186 124 # remember the parts for plotting later 187 125 self.results = [] # type: List[np.ndarray] 126 offset = 2 # skip scale & background 188 127 parts = MixtureParts(self.info, self.kernels, call_details, values) 189 128 for kernel, kernel_details, kernel_values in parts: 190 129 #print("calling kernel", kernel.info.name) 191 130 result = kernel(kernel_details, kernel_values, cutoff, magnetic) 192 result = np.array(result).astype(kernel.dtype) 193 # print(kernel.info.name, result) 194 if self.operation == '+': 195 total += result 196 elif self.operation == '*': 197 if np.all(total) == 0.0: 198 total = result 199 else: 200 total *= result 131 #print(kernel.info.name, result) 132 total += result 201 133 self.results.append(result) 202 134 … … 239 171 240 172 self.part_num += 1 241 self.par_index += info.parameters.npars 242 if self.model_info.operation == '+': 243 self.par_index += 1 # Account for each constituent model's scale param 173 self.par_index += info.parameters.npars + 1 244 174 self.mag_index += 3 * len(info.parameters.magnetism_index) 245 175 … … 252 182 # which includes the initial scale and background parameters. 253 183 # We want the index into the weight length/offset for each parameter. 254 # Exclude the initial scale and background, so subtract two. If we're 255 # building an addition model, each component has its own scale factor 256 # which we need to skip when constructing the details for the kernel, so 257 # add one, giving a net subtract one. 258 diff = 1 if self.model_info.operation == '+' else 2 259 index = slice(par_index - diff, par_index - diff + info.parameters.npars) 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) 260 189 length = full.length[index] 261 190 offset = full.offset[index] … … 267 196 def _part_values(self, info, par_index, mag_index): 268 197 # type: (ModelInfo, int, int) -> np.ndarray 269 # Set each constituent model's scale to 1 if this is a multiplication model 270 scale = self.values[par_index] if self.model_info.operation == '+' else 1.0 271 diff = 1 if self.model_info.operation == '+' else 0 # Skip scale if addition model 272 pars = self.values[par_index + diff:par_index + info.parameters.npars + diff] 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] 273 201 nmagnetic = len(info.parameters.magnetism_index) 274 202 if nmagnetic: -
sasmodels/model_test.py
r65314f7 rbedb9b0 201 201 ({}, 'VR', None), 202 202 ] 203 tests = smoke_tests 204 if self.info.tests is not None: 205 tests += self.info.tests 203 204 tests = smoke_tests + self.info.tests 206 205 try: 207 206 model = build_model(self.info, dtype=self.dtype, … … 372 371 stream.writeln(traceback.format_exc()) 373 372 return 373 374 374 # Run the test suite 375 375 suite.run(result) -
sasmodels/modelinfo.py
r65314f7 r0bdddc2 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 models731 return kernel_module.model_info732 729 info = ModelInfo() 733 730 #print("make parameter table", kernel_module.parameters) -
sasmodels/product.py
r6a5ccfb r8f04da4 77 77 78 78 model_info = ModelInfo() 79 model_info.id = ' @'.join((p_id, s_id))80 model_info.name = ' @'.join((p_name, s_name))79 model_info.id = '*'.join((p_id, s_id)) 80 model_info.name = '*'.join((p_name, s_name)) 81 81 model_info.filename = None 82 82 model_info.title = 'Product of %s and %s'%(p_name, s_name) -
sasmodels/sasview_model.py
rbcdd6c9 r724257c 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) -> SasviewModelType146 """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.filename154 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType155 return ConstructedModel156 157 158 144 def _make_standard_model(name): 159 145 # type: (str) -> SasviewModelType … … 167 153 kernel_module = generate.load_kernel_module(name) 168 154 model_info = modelinfo.make_model_info(kernel_module) 169 return make_model_from_info(model_info)170 171 155 return _make_model_from_info(model_info) 156 157 172 158 def _register_old_models(): 173 159 # type: () -> None … … 201 187 model_info = product.make_product_info(form_factor._model_info, 202 188 structure_factor._model_info) 203 ConstructedModel = make_model_from_info(model_info)189 ConstructedModel = _make_model_from_info(model_info) 204 190 return ConstructedModel() 205 191 192 def _make_model_from_info(model_info): 193 # type: (ModelInfo) -> SasviewModelType 194 """ 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.filename 202 ConstructedModel = type(model_info.name, (SasviewModel,), attrs) # type: SasviewModelType 203 return ConstructedModel 206 204 207 205 def _generate_model_attributes(model_info): … … 605 603 if hasattr(self._model_info, "composition") \ 606 604 and self._model_info.composition is not None: 607 p_model = make_model_from_info(self._model_info.composition[1][0])()608 s_model = make_model_from_info(self._model_info.composition[1][1])()605 p_model = _make_model_from_info(self._model_info.composition[1][0])() 606 s_model = _make_model_from_info(self._model_info.composition[1][1])() 609 607 return p_model, s_model 610 608
Note: See TracChangeset
for help on using the changeset viewer.