Package reflectometry :: Package model1d :: Package model :: Module layer

Source Code for Module reflectometry.model1d.model.layer

  1  """ 
  2  Reflectometry layer times. 
  3   
  4  A reflectometry layer has a function calc which takes a vector z 
  5  and a layer thickness d, returning a profile value for each z: 
  6   
  7     def calc(self, thickness, z): 
  8   
  9  Reflectometry profile interactor. 
 10  """ 
 11   
 12  import numpy 
 13  from bspline import bspline3 
 14   
 15  # ------------------------------------------------------------------ 
 16   
17 -class _Layer:
18 """ Abstract Layer Class """
19 - def __init__(self, 20 name = "NonType", 21 Val = -10000, 22 span = 1 23 ):
24 self._layerName = name 25 self._val = Val 26 self.span = span 27 28 self.toFloatValue() 29 30 # set default is FIXED, not variable 31 self._var = False
32 33
34 - def toFloatValue(self):
35 try: n = len(self._val) 36 except: n = 1 37 if n==1: self._val = float(self._val) 38 else: 39 for i in xrange(n): 40 self._val[i]=float(self._val[i])
41 42
43 - def getValue(self):
44 return self._val
45 46
47 - def setValue(self, value):
48 self._val = value
49 50
51 - def calc(self, thickness, z):
52 """Compute the values for the profile""" 53 pass
54 55
56 - def _toStr():
57 pass
58 59 60 61 # ------------------------------------------------------------------ 62
63 -class FlatLayer(_Layer):
64 """ 65 Flat section of reflectometry profile 66 """
67 - def __init__(self, 68 name = "Flat", 69 Val = 1.0, 70 span = 1 71 ):
72 """ 73 Create a flat layer for a profile. It has a single parameter v 74 indicating the value of the layer. 75 76 The optional span indicates how many model layers are spanned 77 by this portion of the profile. 78 """ 79 _Layer.__init__(self, name, Val, span)
80 81
82 - def calc(self, thickness, z):
83 """Compute the values for the profile at depth z (scalar or array).""" 84 return self._val
85 86
87 - def _toStr(self):
88 return "%.6f"%(self._val)
89 90 91 92 # ------------------------------------------------------------------ 93
94 -class SlopeLayer(_Layer):
95 """ 96 Sloping section of reflectometry profile 97 """
98 - def __init__(self, 99 name = "Slope", 100 Val = 1.0, 101 span = 1 102 ):
103 """ 104 Create a sloping layer for a profile. It has a single tuple v 105 indicating the value at the ends of the layer. 106 107 The optional span indicates how many model layers are spanned 108 by this portion of the profile. 109 110 TODO: line should pick up it's end points from surrounding layers. 111 """ 112 _Layer.__init__(self, name, Val, span)
113 114
115 - def calc(self, 116 thickness, 117 z 118 ):
119 """ 120 Compute the values for the profile at depth z (scalar or array). 121 """ 122 left,right = self._val 123 slope = (right-left)/float(thickness) 124 125 return slope*z + left
126 127
128 - def _toStr(self):
129 return "Slope(%s)" %( self._val )
130 131 132 133 # ------------------------------------------------------------------ 134
135 -class SplineLayer(_Layer):
136 """ 137 Spline section of reflectometry profile 138 """
139 - def __init__(self, 140 name = "spline", 141 Val = 1.0, 142 span = 1, 143 uniform = True 144 ):
145 """ 146 Create a spline layer for a profile. It has a single tuple v 147 indicating the control values of the spline. 148 The control points are assumed to be equally spaced. 149 150 The optional span indicates how many model layers are spanned 151 by this portion of the profile. 152 153 TODO: line should pick up it's end points from surrounding layers. 154 TODO: provide methods for adding/deleting points. 155 """ 156 if Val == None: 157 raise ValueError, "Must support v value's" 158 159 _Layer.__init__(self, name, Val, span) 160 161 self.control = len( Val ) 162 163 if self.control < 3: 164 raise ValueError, "Splines must have at least 3 control points" 165 166 self.control_z = numpy.arange(0.0, self.control)/(self.control-1.0)
167 168 169
170 - def calc(self, 171 thickness, 172 z 173 ):
174 """ 175 Compute the values for the profile at depth z (scalar or array). 176 """ 177 control_rho = numpy.array(self._val) 178 knots = numpy.concatenate( ( [0.0,0.0], self.control_z, [1.0,1.0] ) ) 179 # Convert knots from [0,1] to [0,thickness] 180 knots *= thickness 181 182 if numpy.isscalar(z): 183 # bspline3 works on vectors but profile may accept a scalar 184 return bspline3(knots, control_rho, numpy.array([z]))[0] 185 else: 186 return bspline3(knots, control_rho, z)
187 188
189 - def _toStr(self):
190 return "BSpline(%s)" %(self._val)
191 192 193 # ------------------------------------------------------------------ 194
195 -class CosineLayer(_Layer):
196 """ 197 Cosine section of reflectometry profile 198 """
199 - def __init__(self, 200 name = "Cosine", 201 Val = 1.0, 202 span = 1 203 ):
204 """ 205 Create a Cosine layer for a profile. 206 It has a single tuple v indicating the 'offset', 'amplitude', 207 'wavelength', and 'phase' of the Cosine. 208 209 The optional span indicates how many model layers are spanned 210 by this portion of the profile. 211 """ 212 if Val == None: 213 raise ValueError, "Must support v value's" 214 215 _Layer.__init__(self, name, Val, span) 216 217 self.n = len( Val ) 218 219 if self.n < 4: 220 raise ValueError, "Cosine must have at least 4 parameters"
221 222
223 - def calc(self, 224 thickness, 225 z 226 ):
227 """ 228 Compute the values for the profile at depth z (scalar or array). 229 """ 230 offset,amplitude,wavelength,phase = self._val 231 return offset + \ 232 amplitude*numpy.cos( 2.0*numpy.pi*( (z/wavelength) + phase ) )
233 234
235 - def _toStr(self):
236 return "Cosine(%s)" %(self._val)
237 238 239 240 # ------------------------------------------------------------------ 241
242 -class PowerLawLayer(_Layer):
243 """ 244 PowerLaw section of reflectometry profile 245 """
246 - def __init__(self, 247 name = "Power Law", 248 Val = 1.0, 249 span = 1 250 ):
251 """ 252 Create a PowerLaw layer for a profile. 253 It has a single tuple v indicating the 'multiplier', 'lin_coeff', 254 and 'power' of the PowerLaw. 255 256 The optional span indicates how many model layers are spanned 257 by this portion of the profile. 258 """ 259 if Val == None: 260 raise ValueError, "Must support v value's" 261 262 _Layer.__init__(self, name, Val, span) 263 264 self.n = len( Val ) 265 266 if self.n < 3: 267 raise ValueError, "PowerLaw must have at least 3 parameters"
268 269
270 - def calc(self, 271 thickness, 272 z 273 ):
274 """ 275 Compute the values for the profile at depth z (scalar or array). 276 """ 277 multiplier,lin_coeff,pownum = self._val 278 return multiplier*numpy.power( 1 + (z*lin_coeff), pownum )
279
280 - def _toStr(self):
281 return "PowerLaw(%s)" %(self._val)
282 283 284 285 # ------------------------------------------------------------------ 286
287 -class TetheredPolymerLayer(_Layer):
288 """ 289 TetheredPolymer section of reflectometry profile 290 """
291 - def __init__(self, 292 name = "Tethered Polymer", 293 Val = 1.0, 294 span = 1 295 ):
296 """ 297 Create a Tethered Polymer layer for a profile. 298 It has a single tuple v indicating the 'Value for polymer', 299 'Value for solvent', 'phi_0', 'Flat fraction', and 'Y' 300 of the Tethered Polymer. 301 302 The optional span indicates how many model layers are spanned 303 by this portion of the profile. 304 """ 305 if Val == None: 306 raise ValueError, "Must support v value's" 307 308 _Layer.__init__(self, name, Val, span) 309 310 self.n = len( Val ) 311 312 if self.n < 5: 313 raise ValueError, "PowerLaw must have at least 5 parameters"
314 315
316 - def calc(self, 317 thickness, 318 z 319 ):
320 """ 321 Compute the values for the profile at depth z (scalar or array). 322 """ 323 SLD_polymer, SLD_solvent, L_head, phi0, Y = self._val 324 325 L_tail = thickness - L_head 326 sq_coeffs = (z - L_head) / L_tail 327 sq_coeffs = numpy.clip(sq_coeffs, 0.0, 1.0) 328 power_coeffs = 1.0 - (sq_coeffs ** 2.0) 329 volfrac = phi0 * (power_coeffs ** Y) 330 result = SLD_polymer * volfrac + \ 331 SLD_solvent * (1.0-volfrac) 332 333 #if not numpy.isscalar(z): 334 # print "tether",z[0],result[0],volfrac[0],SLD_polymer,SLD_solvent 335 336 return result
337 338
339 - def _toStr(self):
340 return "TetheredPolymer(%s)" %(self._val)
341 342 343 344 # ------------------------------------------------------------------ 345
346 -class JoinLayer:
347 """ 348 Join the current section of the profile to the previous section 349 """
350 - def __init__(self, span=0):
351 """ 352 Create a placeholder which indicates that the previous layer should be 353 extended by the depth of the current layer. The join layer 354 inherently has a span of 0. 355 """ 356 if span != 0: raise ValueError("Joined layer span must be zero") 357 self.span = 0
358
359 - def calc(self, thickness, z):
360 """ 361 A join layer has no profile and should not be computed. 362 """ 363 raise NotImplementedError("Joined layer has no profile")
364 365 366 367 # ------------------------------------------------------------------ 368
369 -class NoLayer:
370 """ 371 Empty layer 372 """
373 - def __init__(self, span=1):
374 """ 375 Create a placeholder which indicates that the current layer is empty. 376 This will evaluate as a flat layer of value 0. 377 378 The optional span indicates how many model layers are spanned 379 by this portion of the profile. 380 """ 381 self.span = span
382
383 - def calc(self, thickness, z):
384 """ 385 Compute the values for the profile at depth z (scalar or array). 386 """ 387 return z*0
388 389 390 391 392 393 # ============== Factory for producing layer objects ===========
394 -def Layer(v, **kw):
395 """ 396 Construct a layer a simple description. Use 0 for NoLayer, 397 a value for a flat layer, a pair for a sloping layer or a 398 long list for a spline layer. 399 """ 400 if isinstance(v, (int,float)): 401 #if isinstance(v, (list) ): 402 403 if v == 10000: 404 # Why NoLayer type? 405 return NoLayer() 406 else: 407 return FlatLayer( Val = v ) 408 409 else: 410 if hasattr(v, 'build' ): 411 412 if len(v.run()[0]) == 2: 413 414 return SlopeLayer( Val = v.run()[0] ) 415 416 elif len(v.run()[0]) >= 3 and v.build()[:7]=="BSpline": 417 418 return SplineLayer( Val = v.run()[0] ) 419 420 elif len(v.run()[0]) >= 3 and v.build()[:8]=="Tethered": 421 422 return TetheredPolymerLayer( Val = v.run()[0] ) 423 else: 424 pass 425 426 else: 427 pass
428