[ae3ce4e] | 1 | |
---|
| 2 | from sans.models.ModelFactory import ModelFactory |
---|
| 3 | from sans.models.BaseComponent import BaseComponent |
---|
| 4 | import math |
---|
| 5 | |
---|
| 6 | class Smear: |
---|
| 7 | |
---|
| 8 | def __init__(self, model, param, sigma): |
---|
| 9 | """ |
---|
| 10 | @param model: model to smear |
---|
| 11 | @param param: parameter to smear |
---|
| 12 | @param sigma: std deviations for parameter |
---|
| 13 | """ |
---|
| 14 | |
---|
| 15 | |
---|
| 16 | ## Model to smear |
---|
| 17 | #self.model = factory.getModel(model_name) |
---|
| 18 | self.model = model |
---|
| 19 | ## Standard deviation of the smearing |
---|
| 20 | self.sigmas = sigma |
---|
| 21 | ## Parameter to smear |
---|
| 22 | self.params = param |
---|
| 23 | ## Nominal value of the smeared parameter |
---|
| 24 | self.centers = [] |
---|
| 25 | ## Error on last evaluation |
---|
| 26 | self.error = 0.0 |
---|
| 27 | ## Error flag |
---|
| 28 | self.doErrors = False |
---|
| 29 | for par in self.params: |
---|
| 30 | self.centers.append(self.model.getParam(par)) |
---|
| 31 | |
---|
| 32 | def smearParam(self, id, x): |
---|
| 33 | """ |
---|
| 34 | @param x: input value |
---|
| 35 | """ |
---|
| 36 | # from random import random |
---|
| 37 | # If we exhausted the parameter array, simply evaluate |
---|
| 38 | # the model |
---|
| 39 | if id < len(self.params): |
---|
| 40 | #print "smearing", self.params[id] |
---|
| 41 | |
---|
| 42 | # Average over Gaussian distribution (2 sigmas) |
---|
| 43 | value_sum = 0.0 |
---|
| 44 | gauss_sum = 0.0 |
---|
| 45 | |
---|
| 46 | min_value = self.centers[id] - 2*self.sigmas[id] |
---|
| 47 | max_value = self.centers[id] + 2*self.sigmas[id] |
---|
| 48 | n_pts = 25 |
---|
| 49 | step = (max_value - min_value)/(n_pts-1) |
---|
| 50 | #print min_value, max_value, step, max_value-min_value |
---|
| 51 | if step == 0.0: |
---|
| 52 | return self.smearParam(id+1,x) |
---|
| 53 | |
---|
| 54 | # Gaussian function used to weigh points |
---|
| 55 | #gaussian = ModelFactory().getModel("Gaussian") |
---|
| 56 | gaussian = Gaussian() |
---|
| 57 | gaussian.setParam('sigma', self.sigmas[id]) |
---|
| 58 | gaussian.setParam('mean', self.centers[id]) |
---|
| 59 | |
---|
| 60 | # Compute average |
---|
| 61 | prev_value = None |
---|
| 62 | error_sys = 0.0 |
---|
| 63 | for i in range(n_pts): |
---|
| 64 | # Set the parameter value |
---|
| 65 | value = min_value + i*step |
---|
| 66 | # value = random()*4.0*self.sigmas[id] + min_value |
---|
| 67 | # print value |
---|
| 68 | self.model.setParam(self.params[id], value) |
---|
| 69 | gauss_value = gaussian.run(value) |
---|
| 70 | #gauss_value = 1.0 |
---|
| 71 | #if id==0: print value, gauss_value |
---|
| 72 | func_value, error_1 = self.smearParam(id+1, x) |
---|
| 73 | if self.doErrors: |
---|
| 74 | if not prev_value == None: |
---|
| 75 | error_sys += (func_value-prev_value)*(func_value-prev_value)/4.0 |
---|
| 76 | prev_value = func_value |
---|
| 77 | |
---|
| 78 | value_sum += gauss_value * func_value |
---|
| 79 | gauss_sum += gauss_value |
---|
| 80 | |
---|
| 81 | #print "Error", math.sqrt(error) |
---|
| 82 | return value_sum/gauss_sum, math.sqrt(error_sys) |
---|
| 83 | |
---|
| 84 | else: |
---|
| 85 | return self.model.run(x), 0.0 |
---|
| 86 | |
---|
| 87 | def run(self, x): |
---|
| 88 | """ |
---|
| 89 | @param x: input |
---|
| 90 | """ |
---|
| 91 | |
---|
| 92 | # Go through the list of parameters |
---|
| 93 | n_par = len(self.params) |
---|
| 94 | |
---|
| 95 | # Check array lengths |
---|
| 96 | if not len(self.centers) == n_par or\ |
---|
| 97 | not len(self.sigmas) == n_par: |
---|
| 98 | raise ValueError, "Incompatible array lengths" |
---|
| 99 | |
---|
| 100 | # Smear first parameters |
---|
| 101 | if n_par > 0: |
---|
| 102 | value, error = self.smearParam(0, x) |
---|
| 103 | self.error = error |
---|
| 104 | |
---|
| 105 | # Put back original values |
---|
| 106 | for i in range(len(self.centers)): |
---|
| 107 | self.model.setParam(self.params[i], self.centers[i]) |
---|
| 108 | |
---|
| 109 | |
---|
| 110 | return value |
---|
| 111 | |
---|
| 112 | class Gaussian(BaseComponent): |
---|
| 113 | """ Gaussian function """ |
---|
| 114 | |
---|
| 115 | def __init__(self): |
---|
| 116 | """ Initialization """ |
---|
| 117 | BaseComponent.__init__(self) |
---|
| 118 | |
---|
| 119 | ## Name of the model |
---|
| 120 | self.name = "Gaussian" |
---|
| 121 | ## Scale factor |
---|
| 122 | self.params['scale'] = 1.0 |
---|
| 123 | ## Mean value |
---|
| 124 | self.params['mean'] = 0.0 |
---|
| 125 | ## Standard deviation |
---|
| 126 | self.params['sigma'] = 1.0 |
---|
| 127 | ## Internal log |
---|
| 128 | self.log = {} |
---|
| 129 | return |
---|
| 130 | |
---|
| 131 | def run(self, x=0): |
---|
| 132 | """ Evaluate the function |
---|
| 133 | @param x: input q or [q,phi] |
---|
| 134 | @return: scattering function |
---|
| 135 | """ |
---|
| 136 | if(type(x)==type([])): |
---|
| 137 | # vector input |
---|
| 138 | if(len(x)==2): |
---|
| 139 | return self.analytical_2D(x) |
---|
| 140 | else: |
---|
| 141 | raise ValueError, "Gaussian takes a scalar or a 2D point" |
---|
| 142 | else: |
---|
| 143 | return self.analytical_1D(x) |
---|
| 144 | |
---|
| 145 | def analytical_2D(self, x): |
---|
| 146 | """ Evaluate 2D model |
---|
| 147 | @param x: input [q,phi] |
---|
| 148 | @return: scattering function |
---|
| 149 | """ |
---|
| 150 | |
---|
| 151 | # 2D sphere is the same as 1D sphere |
---|
| 152 | return self.analytical_1D(x[0]) |
---|
| 153 | |
---|
| 154 | def analytical_1D(self, x): |
---|
| 155 | """ Evaluate 1D model |
---|
| 156 | @param x: input q |
---|
| 157 | @return: scattering function |
---|
| 158 | """ |
---|
| 159 | vary = x-self.params['mean'] |
---|
| 160 | expo_value = -vary*vary/(2*self.params['sigma']*self.params['sigma']) |
---|
| 161 | value = self.params['scale'] * math.exp(expo_value) |
---|
| 162 | |
---|
| 163 | return value |
---|
| 164 | |
---|
| 165 | # main |
---|
| 166 | if __name__ == '__main__': |
---|
| 167 | import math |
---|
| 168 | if True: |
---|
| 169 | cyl = ModelFactory().getModel("CylinderModel") |
---|
| 170 | cyl.setParam('length', 2000.0) |
---|
| 171 | cyl.setParam('cyl_phi', .45) |
---|
| 172 | cyl.setParam('cyl_theta', 2.) |
---|
| 173 | #app = Smear(cyl,['cyl_theta', 'cyl_phi'], [.01, .01]) |
---|
| 174 | #app = Smear(cyl,['cyl_theta', 'cyl_phi'], [2.0, 2.0]) |
---|
| 175 | #app = Smear(cyl,['cyl_theta'], [2.5]) |
---|
| 176 | app = Smear(cyl, ['cyl_phi', 'cyl_theta'], [math.pi, math.pi]) |
---|
| 177 | #app = Smear(cyl,['scale', 'scale'],[.5,1.0]) |
---|
| 178 | val_no = cyl.run([.001, 1.0]) |
---|
| 179 | print "Cylinder (no smear) f(.1):", val_no |
---|
| 180 | val_sm = app.run([.001, 1.0]) |
---|
| 181 | print "Cylinder (smear) f(.1):", val_sm, app.error, app.error/val_sm |
---|
| 182 | print "Cylinder (1D) f(.1):", cyl.run(.001), cyl.run(.001)/val_sm |
---|
| 183 | |
---|
| 184 | |
---|
| 185 | print "--------------" |
---|
| 186 | sinsin = ModelFactory().getModel("SinSin") |
---|
| 187 | sinsin.setParam('a', -1.7) |
---|
| 188 | sinsin.setParam('b', 5.45) |
---|
| 189 | #app = Smear(sinsin, ['A'], [3.1]) |
---|
| 190 | #app = Smear(sinsin, ['A', 'b'], [math.pi/2.0, .0]) |
---|
| 191 | app = Smear(sinsin, ['A'], [math.pi/2.0]) |
---|
| 192 | #app = Smear(cyl,['scale', 'scale'],[.5,1.0]) |
---|
| 193 | val_no = sinsin.run(1.0) |
---|
| 194 | print "SinSin (no smear) :", val_no |
---|
| 195 | val_sm = app.run(1.0) |
---|
| 196 | print "SinSin (smear) : %g +- %g" % (val_sm, app.error) |
---|
| 197 | |
---|