# A sample of an experimental model function for Sum(Pmodel1,Pmodel2) import copy from sans.models.pluginmodel import Model1DPlugin # User can change the name of the model (only with single functional model) from sans.models.CylinderModel import CylinderModel as P1 from sans.models.PolymerExclVolume import PolymerExclVolume as P2 class Model(Model1DPlugin): """ Use for p1(Q)+p2(Q); Note: P(Q) refers to 'form factor' model. """ name = "" def __init__(self): Model1DPlugin.__init__(self, name='') """ :param p_model1: a form factor, P(Q) :param p_model2: another form factor, P(Q) """ p_model1 = P1() p_model2 = P2() ## Setting model name model description self.description="" self.name = "Sum[" + "P1(Cyl)" +", "+ "P2(PEV)" + "]" self.description = p_model1.name+"\n" self.description += p_model2.name+"\n" self.fill_description(p_model1, p_model2) ## Define parameters self.params = {} ## Parameter details [units, min, max] self.details = {} # non-fittable parameters self.non_fittable = p_model1.non_fittable self.non_fittable += p_model2.non_fittable ##models self.p_model1= p_model1 self.p_model2= p_model2 ## dispersion self._set_dispersion() ## Define parameters self._set_params() ## New parameter:Scaling factor self.params['scale_factor'] = 1 ## Parameter details [units, min, max] self._set_details() self.details['scale_factor'] = ['', None, None] #list of parameter that can be fitted self._set_fixed_params() ## parameters with orientation for item in self.p_model1.orientation_params: new_item = "p1_" + item if not new_item in self.orientation_params: self.orientation_params.append(new_item) for item in self.p_model2.orientation_params: new_item = "p2_" + item if not new_item in self.orientation_params: self.orientation_params.append(new_item) # get multiplicity if model provide it, else 1. try: multiplicity1 = p_model1.multiplicity try: multiplicity2 = p_model2.multiplicity except: multiplicity2 = 1 except: multiplicity1 = 1 multiplicity2 = 1 ## functional multiplicity of the model self.multiplicity1 = multiplicity1 self.multiplicity2 = multiplicity2 self.multiplicity_info = [] def _clone(self, obj): """ Internal utility function to copy the internal data members to a fresh copy. """ obj.params = copy.deepcopy(self.params) obj.description = copy.deepcopy(self.description) obj.details = copy.deepcopy(self.details) obj.dispersion = copy.deepcopy(self.dispersion) obj.p_model1 = self.p_model1.clone() obj.p_model2 = self.p_model2.clone() #obj = copy.deepcopy(self) return obj def _set_dispersion(self): """ combined the two models dispersions Polydispersion should not be applied to s_model """ ##set dispersion only from p_model for name , value in self.p_model1.dispersion.iteritems(): #if name.lower() not in self.p_model1.orientation_params: new_name = "p1_" + name self.dispersion[new_name]= value for name , value in self.p_model2.dispersion.iteritems(): #if name.lower() not in self.p_model2.orientation_params: new_name = "p2_" + name self.dispersion[new_name]= value def function(self, x=0.0): """ """ return 0 def getProfile(self): """ Get SLD profile of p_model if exists : return: (r, beta) where r is a list of radius of the transition points beta is a list of the corresponding SLD values : Note: This works only for func_shell# = 2 (exp function) and is not supporting for p2 """ try: x,y = self.p_model1.getProfile() except: x = None y = None return x, y def _set_params(self): """ Concatenate the parameters of the two models to create this model parameters """ for name , value in self.p_model1.params.iteritems(): # No 2D-supported #if name not in self.p_model1.orientation_params: new_name = "p1_" + name self.params[new_name]= value for name , value in self.p_model2.params.iteritems(): # No 2D-supported #if name not in self.p_model2.orientation_params: new_name = "p2_" + name self.params[new_name]= value # Set "scale" as initializing self._set_scale_factor() def _set_details(self): """ Concatenate details of the two models to create this model details """ for name ,detail in self.p_model1.details.iteritems(): new_name = "p1_" + name #if new_name not in self.orientation_params: self.details[new_name]= detail for name ,detail in self.p_model2.details.iteritems(): new_name = "p2_" + name #if new_name not in self.orientation_params: self.details[new_name]= detail def _set_scale_factor(self): """ Not implemented """ pass def setParam(self, name, value): """ Set the value of a model parameter :param name: name of the parameter :param value: value of the parameter """ # set param to p1+p2 model self._setParamHelper(name, value) ## setParam to p model model_pre = name.split('_', 1)[0] new_name = name.split('_', 1)[1] if model_pre == "p1": if new_name in self.p_model1.getParamList(): self.p_model1.setParam(new_name, value) elif model_pre == "p2": if new_name in self.p_model2.getParamList(): self.p_model2.setParam(new_name, value) elif name.lower() == 'scale_factor': self.params['scale_factor'] = value else: raise ValueError, "Model does not contain parameter %s" % name def getParam(self, name): """ Set the value of a model parameter :param name: name of the parameter """ # Look for dispersion parameters toks = name.split('.') if len(toks)==2: for item in self.dispersion.keys(): # 2D not supported if item.lower()==toks[0].lower():# and \ #item.lower() not in self.orientation_params \ #and toks[0].lower() not in self.orientation_params: for par in self.dispersion[item]: if par.lower() == toks[1].lower(): return self.dispersion[item][par] else: # Look for standard parameter for item in self.params.keys(): if item.lower()==name.lower():#and \ #item.lower() not in self.orientation_params \ #and toks[0].lower() not in self.orientation_params: return self.params[item] return #raise ValueError, "Model does not contain parameter %s" % name def _setParamHelper(self, name, value): """ Helper function to setparam """ # Look for dispersion parameters toks = name.split('.') if len(toks)== 2: for item in self.dispersion.keys(): if item.lower()== toks[0].lower():# and \ #item.lower() not in self.orientation_params: for par in self.dispersion[item]: if par.lower() == toks[1].lower():#and \ #item.lower() not in self.orientation_params: self.dispersion[item][par] = value return else: # Look for standard parameter for item in self.params.keys(): if item.lower()== name.lower():#and \ #item.lower() not in self.orientation_params: self.params[item] = value return raise ValueError, "Model does not contain parameter %s" % name def _set_fixed_params(self): """ fill the self.fixed list with the p_model fixed list """ for item in self.p_model1.fixed: new_item = "p1" + item self.fixed.append(new_item) for item in self.p_model2.fixed: new_item = "p2" + item self.fixed.append(new_item) self.fixed.sort() def run(self, x = 0.0): """ Evaluate the model :param x: input q-value (float or [float, float] as [r, theta]) :return: (scattering function value) """ self._set_scale_factor() return self.params['scale_factor'] * \ (self.p_model1.run(x) + self.p_model2.run(x)) def runXY(self, x = 0.0): """ Evaluate the model :param x: input q-value (float or [float, float] as [qx, qy]) :return: scattering function value """ self._set_scale_factor() return self.params['scale_factor'] * \ (self.p_model1.runXY(x) + self.p_model2.runXY(x)) ## Now (May27,10) directly uses the model eval function ## instead of the for-loop in Base Component. def evalDistribution(self, x = []): """ Evaluate the model in cartesian coordinates :param x: input q[], or [qx[], qy[]] :return: scattering function P(q[]) """ self._set_scale_factor() return self.params['scale_factor'] * \ (self.p_model1.evalDistribution(x) + \ self.p_model2.evalDistribution(x)) def set_dispersion(self, parameter, dispersion): """ Set the dispersion object for a model parameter :param parameter: name of the parameter [string] :dispersion: dispersion object of type DispersionModel """ value= None new_pre = parameter.split("_", 1)[0] new_parameter = parameter.split("_", 1)[1] try: if new_pre == 'p1' and \ new_parameter in self.p_model1.dispersion.keys(): value= self.p_model1.set_dispersion(new_parameter, dispersion) if new_pre == 'p2' and \ new_parameter in self.p_model2.dispersion.keys(): value= self.p_model2.set_dispersion(new_parameter, dispersion) self._set_dispersion() return value except: raise def fill_description(self, p_model1, p_model2): """ Fill the description for P(Q)+P(Q) """ description = "" description +="This model gives the summation of %s and %s.\n"% \ ( p_model1.name, p_model2.name ) self.description += description if __name__ == "__main__": m1= Model() m1.setParam("p1_scale", 25) m1.setParam("p1_long_c", 1000) m1.setParam("p2_scale", 100) m1.setParam("p2_rg", 100) out1 = m1.runXY(0.01) m2= Model() m2.p_model1.setParam("scale", 25) m2.p_model1.setParam("long_c", 1000) m2.p_model2.setParam("scale", 100) m2.p_model2.setParam("rg", 100) out2 = m2.p_model1.runXY(0.01) + m2.p_model2.runXY(0.01) print out1, " = ", out2