def structure(self, structure): self._structure = structure p = Parameters(name="instrument parameters") p.extend([self.scale, self.bkg, self.dq, self.q_offset]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters])
def structure(self, structure): self._structure = structure p = Parameters(name='instrument parameters') p.extend([self.scale, self.bkg, self.dq]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters])
def parameters(self): p = Parameters(name=self.name) p.extend([ self.polymer_sld.parameters, self.phi_0, self.gamma, self.alpha, self.delta, self.rough ]) return p
def parameters(self): p = Parameters(name=self.name) p.extend([ self.extent, self.dz, self.vs, self.left_slab.parameters, self.right_slab.parameters, self.solvent.parameters ]) return p
def structure(self, structure): self._structure = structure p = Parameters(name="instrument parameters") p.extend([self.wav, self.delOffset]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters])
def parameters(self): p = Parameters(name=self.name) p.extend([self.apm, self.b_heads_real, self.b_heads_imag, self.vm_heads, self.thickness_heads, self.b_tails_real, self.b_tails_imag, self.vm_tails, self.thickness_tails, self.rough_head_tail, self.rough_preceding_mono]) return p
def parameters(self): p = Parameters(name=self.name) p.extend([self.apm, self.nWater, self.b_heads_real, self.b_heads_imag, self.vm_heads, self.thickness_heads, self.b_tails_real, self.b_tails_imag, self.vm_tails, self.thickness_tails, self.roughness, self.solv_rough]) return p
def parameters(self): p = Parameters(name=self.name) p.extend([ self.b_heads_real, self.b_heads_imag, self.b_tails_real, self.b_tails_imag, self.thick_heads, self.thick_tails, self.tail_mol_vol, self.head_mol_vol, self.rough_head_tail, self.rough_preceding_mono, self.phit, self.phih ]) return p
class SLD(object): """ Object representing freely varying SLD of a material Parameters ---------- value : float or complex Scattering length density of a material. Units (10**-6 Angstrom**-2) name : str, optional Name of material. Notes ----- An SLD object can be used to create a Slab: >>> # an SLD object representing Silicon Dioxide >>> sio2 = SLD(3.47, name='SiO2') >>> # create a Slab of SiO2 20 A in thickness, with a 3 A roughness >>> sio2_layer = SLD(20, 3) """ def __init__(self, value, name=''): self.name = name if isinstance(value, complex): self.real = Parameter(value.real, name='%s - sld' % name) self.imag = Parameter(value.imag, name='%s - isld' % name) elif isinstance(value, SLD): self.real = value.real self.imag = value.imag else: self.real = Parameter(value, name='%s - sld' % name) self.imag = Parameter(0, name='%s - isld' % name) self._parameters = Parameters(name=name) self._parameters.extend([self.real, self.imag]) def __repr__(self): sld = complex(self.real.value, self.imag.value) return 'SLD = {0} x10**-6 Å**-2'.format(sld) def __call__(self, thick=0, rough=0): return Slab(thick, self, rough, name=self.name) def __or__(self, other): # c = self | other slab = self() return slab | other @property def parameters(self): """ :class:`refnx.analysis.Parameters` associated with this component """ self._parameters.name = self.name return self._parameters
def parameters(self): r""" :class:`refnx.analysis.Parameters`, all the parameters associated with this structure. """ p = Parameters(name='Stack - {0}'.format(self.name)) p.append(self.repeats) p.extend([component.parameters for component in self.components]) return p
def parameters(self): r""" :class:`refnx.analysis.Parameters`, all the parameters associated with this structure. """ p = Parameters(name='Structure - {0}'.format(self.name)) p.extend([component.parameters for component in self.components]) if self._solvent is not None: p.append(self.solvent.parameters) return p
def parameters(self): p = Parameters(name=self.name) p.extend([ self.interface_air_protein, self.interface_protein_solvent, self.protein_length, self.number_of_water_molecules, self.interface_width_air_solvent, #self.interface_width_protein_solvent, self.sld_of_protein, self.d2o_to_h2o_ratio ]) return p
def parameters(self): p = Parameters(name=self.name) p.extend([ self.apm, self.b_heads_real, self.b_heads_imag, self.vm_heads, self.thickness_heads, self.b_tails_real, self.b_tails_imag, self.vm_tails, self.thickness_tails, self.rough_head_tail, self.rough_preceding_mono ]) if self.head_solvent is not None: p.append(self.head_solvent.parameters) if self.tail_solvent is not None: p.append(self.tail_solvent.parameters) return p
def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ p = Parameters(name='instrument parameters') p.extend([self.scales, self.bkg, self.dq]) self._parameters = Parameters(name=self.name) self._parameters.append([p]) self._parameters.extend( [structure.parameters for structure in self._structures]) return self._parameters
def parameters(self): para = Parameters(name=self.name) para.extend([ self.water_per_lipid_head, self.water_per_lipid_tail, self.b_h, self.bi_h, self.b_t, self.bi_t, self.apm, self.roughness, self.volume_heads, self.volume_tails, ]) return para
def parameters(self): p = Parameters(name=self.name) p.extend([self.thick]) # p.extend(self.sld.parameters) p.extend([self.rough, self.vol_protein, self.vfsolv]) # p.name = self.name # p.extend([self.vol_protein, # self.PLratio])#,self.solventSLD # if isinstance(solventSLD, SLD): # p.extend([self.solventSLD.parameters]) # elif isinstance(solventSLD, complex): # self.solventSLD = SLD(solventSLD) # else: # p.extend([self.solventSLD return p
def parameters(self): p = Parameters(name=self.name) p.extend([ self.Popc.s_sld, self.Popc.water_per_lipid_head, self.Popc.water_per_lipid_tail, self.Popc.b_heads_real, self.Popc.b_heads_imag, self.Popc.b_tails_real, self.Popc.b_tails_imag, self.Popc.vm_heads, self.Popc.vm_tails, self.Popg.s_sld, self.Popg.water_per_lipid_head, self.Popg.water_per_lipid_tail, self.Popg.b_heads_real, self.Popg.b_heads_imag, self.Popg.b_tails_real, self.Popg.b_tails_imag, self.Popg.vm_heads, self.Popg.vm_tails, self.apm, self.roughness_top, self.roughness_bottom, self.ratio, self.volFrac ]) return p # def logp(self): # return 0
def __init__(self, thick, sld, rough, name='', vfsolv=0, interface=None): super(Slab, self).__init__(name=name) self.thick = possibly_create_parameter(thick, name='%s - thick' % name) if isinstance(sld, SLD): self.sld = sld else: self.sld = SLD(sld) self.rough = possibly_create_parameter(rough, name='%s - rough' % name) self.vfsolv = (possibly_create_parameter(vfsolv, name='%s - volfrac solvent' % name)) p = Parameters(name=self.name) p.extend([ self.thick, self.sld.real, self.sld.imag, self.rough, self.vfsolv ]) self._parameters = p self.interfaces = interface
def parameters(self): p = Parameters(name=self.name) p.extend([self.gamma, self.dz, self.vf, self.polymer_sld.parameters]) p.extend([slab.parameters for slab in self.left_slabs]) p.extend([slab.parameters for slab in self.right_slabs]) return p
def parameters(self): p = Parameters(name=self.name) p.extend([self.adsorbed_amount, self.dzf, self.vff, self.polymer_sld.parameters]) p.extend([slab.parameters for slab in self.left_slabs]) p.extend([slab.parameters for slab in self.right_slabs]) return p
def parameters(self): """ Returns ------- Parameter, array_like An array of the parameters in the fitting. """ p = Parameters(name=self.name) p.extend([ self.vol[0], self.vol[1], self.realb[0], self.realb[1], self.imagb[0], self.imagb[1], self.d[0], self.d[1], self.phi[0], self.phi[1], self.sigma, ]) return p
def parameters(self): """ Parameters for the model. Returns: (refnx.analysis.Parameters) Model parameters. """ para = Parameters(name=self.name) para.extend([ self.b_real_h, self.b_imag_h, self.b_real_t, self.b_imag_t, self.thick_h, self.thick_t, self.mol_vol_h, self.mol_vol_t, self.rough_h_t, self.rough_t_a, self.phi_h, self.phi_t, ]) return para
def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ p = Parameters(name='meta instrument parameters') p.extend([self.bkg, self.dq]) p.extend(self.additional_params) self._parameters = Parameters(name=self.name) for model, scale in zip(self.models, self._scales): p.extend([scale]) p.extend(model.parameters.flattened()) self._parameters.append(p) return self._parameters
class SLD(object): """ Object representing freely varying SLD of a material Parameters ---------- value : float, complex, Parameter, Parameters Scattering length density of a material. Units (10**-6 Angstrom**-2) name : str, optional Name of material. Notes ----- An SLD object can be used to create a Slab: >>> # an SLD object representing Silicon Dioxide >>> sio2 = SLD(3.47, name='SiO2') >>> # create a Slab of SiO2 20 A in thickness, with a 3 A roughness >>> sio2_layer = sio2(20, 3) The SLD object can also be made from a complex number, or from Parameters >>> sio2 = SLD(3.47+0.01j) >>> re = Parameter(3.47) >>> im = Parameter(0.01) >>> sio2 = SLD(re) >>> sio2 = SLD([re, im]) """ def __init__(self, value, name=''): self.name = name self.imag = Parameter(0, name='%s - isld' % name) if isinstance(value, numbers.Real): self.real = Parameter(value.real, name='%s - sld' % name) elif isinstance(value, numbers.Complex): self.real = Parameter(value.real, name='%s - sld' % name) self.imag = Parameter(value.imag, name='%s - isld' % name) elif isinstance(value, SLD): self.real = value.real self.imag = value.imag elif isinstance(value, Parameter): self.real = value elif (hasattr(value, '__len__') and isinstance(value[0], Parameter) and isinstance(value[1], Parameter)): self.real = value[0] self.imag = value[1] self._parameters = Parameters(name=name) self._parameters.extend([self.real, self.imag]) def __repr__(self): return ("SLD([{real!r}, {imag!r}]," " name={name!r})".format(**self.__dict__)) def __str__(self): sld = complex(self.real.value, self.imag.value) return 'SLD = {0} x10**-6 Å**-2'.format(sld) def __complex__(self): return complex(self.real.value, self.imag.value) def __call__(self, thick=0, rough=0): """ Create a :class:`Slab`. Parameters ---------- thick: refnx.analysis.Parameter or float Thickness of slab in Angstrom rough: refnx.analysis.Parameter or float Roughness of slab in Angstrom Returns ------- slab : refnx.reflect.Slab The newly made Slab. Example -------- >>> # an SLD object representing Silicon Dioxide >>> sio2 = SLD(3.47, name='SiO2') >>> # create a Slab of SiO2 20 A in thickness, with a 3 A roughness >>> sio2_layer = sio2(20, 3) """ return Slab(thick, self, rough, name=self.name) def __or__(self, other): # c = self | other slab = self() return slab | other @property def parameters(self): """ :class:`refnx.analysis.Parameters` associated with this component """ self._parameters.name = self.name return self._parameters
def parameters(self): p = Parameters(name=self.name) p.extend([self.extent, self.decay, self.rough]) return p
class MixedReflectModel(object): r""" Calculates an incoherent average of reflectivities from a sequence of structures. Such a situation may occur if a sample is not uniform over its illuminated area. Parameters ---------- structures : sequence of refnx.reflect.Structure The interfacial structures to incoherently average scales : None, sequence of float or refnx.analysis.Parameter, optional scale factors. The reflectivities calculated from each of the structures are multiplied by their respective scale factor during overall summation. These values are turned into Parameters during the construction of this object. You must supply a scale factor for each of the structures. If `scales` is `None`, then default scale factors are used: `[1 / len(structures)] * len(structures)`. It is a good idea to set the lower bound of each scale factor to zero (not done by default). bkg : float or refnx.analysis.Parameter, optional linear background added to the overall reflectivity. This is turned into a Parameter during the construction of this object. name : str, optional Name of the mixed Model dq : float or refnx.analysis.Parameter, optional - `dq == 0` then no resolution smearing is employed. - `dq` is a float or refnx.analysis.Parameter a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. However, if `x_err` is supplied to the `model` method, then that overrides any setting given here. This value is turned into a Parameter during the construction of this object. threads: int, optional Specifies the number of threads for parallel calculation. This option is only applicable if you are using the ``_creflect`` module. The option is ignored if using the pure python calculator, ``_reflect``. If `threads == -1` then all available processors are used. quad_order: int, optional the order of the Gaussian quadrature polynomial for doing the resolution smearing. default = 17. Don't choose less than 13. If quad_order == 'ultimate' then adaptive quadrature is used. Adaptive quadrature will always work, but takes a _long_ time (2 or 3 orders of magnitude longer). Fixed quadrature will always take a lot less time. BUT it won't necessarily work across all samples. For example, 13 points may be fine for a thin layer, but will be atrocious at describing a multilayer with bragg peaks. dq_type: {'pointwise', 'constant'}, optional Chooses whether pointwise or constant dQ/Q resolution smearing (see `dq` keyword) is used. To use pointwise smearing the `x_err` keyword provided to `Objective.model` method must be an array, otherwise the smearing falls back to 'constant'. q_offset: float or refnx.analysis.Parameter, optional Compensates for uncertainties in the angle at which the measurement is performed. A positive/negative `q_offset` corresponds to a situation where the measured q values (incident angle) may have been under/over estimated, and has the effect of shifting the calculated model to lower/higher effective q values. """ def __init__( self, structures, scales=None, bkg=1e-7, name="", dq=5.0, threads=-1, quad_order=17, dq_type="pointwise", q_offset=0.0, ): self.name = name self._parameters = None self.threads = threads self.quad_order = quad_order # all reflectometry models need a scale factor and background. Set # them all to 1 by default. pscales = Parameters(name="scale factors") if scales is not None and len(structures) == len(scales): tscales = scales elif scales is not None and len(structures) != len(scales): raise ValueError( "You need to supply scale factor for each" " structure" ) else: tscales = [1 / len(structures)] * len(structures) for scale in tscales: p_scale = possibly_create_parameter(scale, name="scale") pscales.append(p_scale) self._scales = pscales self._bkg = possibly_create_parameter(bkg, name="bkg") # we can optimize the resolution (but this is always overridden by # x_err if supplied. There is therefore possibly no dependence on it. self._dq = possibly_create_parameter(dq, name="dq - resolution") self.dq_type = dq_type self._q_offset = possibly_create_parameter(q_offset, name="q_offset") self._structures = structures def __repr__(self): s = ( f"MixedReflectModel({self._structures!r}," f" scales={self._scales!r}, bkg={self._bkg!r}," f" name={self.name!r}, dq={self._dq!r}," f" threads={self.threads!r}, quad_order={self.quad_order!r}," f" dq_type={self.dq_type!r}, q_offset={self.q_offset!r})" ) return s def __call__(self, x, p=None, x_err=None): r""" Calculate the generative model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ return self.model(x, p=p, x_err=x_err) @property def dq(self): r""" :class:`refnx.analysis.Parameter` - `dq.value == 0` no resolution smearing is employed. - `dq.value > 0` a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. However, if `x_err` is supplied to the `model` method, then that overrides any setting reported here. """ return self._dq @dq.setter def dq(self, value): self._dq.value = value @property def q_offset(self): r""" :class:`refnx.analysis.Parameter` - compensates for any angular misalignment during an experiment. """ return self._q_offset @q_offset.setter def q_offset(self, value): self._q_offset.value = value @property def scales(self): r""" :class:`refnx.analysis.Parameter` - the reflectivity from each of the structures are multiplied by these values to account for patchiness. """ return self._scales @property def bkg(self): r""" :class:`refnx.analysis.Parameter` - linear background added to all model values. """ return self._bkg @bkg.setter def bkg(self, value): self._bkg.value = value def model(self, x, p=None, x_err=None): r""" Calculate the reflectivity of this model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameter, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray """ if p is not None: self.parameters.pvals = np.array(p) if x_err is None or self.dq_type == "constant": # fallback to what this object was constructed with x_err = float(self.dq) scales = np.array(self.scales) y = np.zeros_like(x) for scale, structure in zip(scales, self.structures): y += reflectivity( x + self.q_offset.value, structure.slabs()[..., :4], scale=scale, dq=x_err, threads=self.threads, quad_order=self.quad_order, ) return y + self.bkg.value def logp(self): r""" Additional log-probability terms for the reflectivity model. Do not include log-probability terms for model parameters, these are automatically calculated elsewhere. Returns ------- logp : float log-probability of structure. """ logp = 0 for structure in self._structures: logp += structure.logp() return logp @property def structures(self): r""" list of :class:`refnx.reflect.Structure` that describe the patchiness of the surface. """ return self._structures @property def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ p = Parameters(name="instrument parameters") p.extend([self.scales, self.bkg, self.dq, self.q_offset]) self._parameters = Parameters(name=self.name) self._parameters.append([p]) self._parameters.extend( [structure.parameters for structure in self._structures] ) return self._parameters
class ReflectModel(object): r""" Parameters ---------- structure : refnx.reflect.Structure The interfacial structure. scale : float or refnx.analysis.Parameter, optional scale factor. All model values are multiplied by this value before the background is added. This is turned into a Parameter during the construction of this object. bkg : float or refnx.analysis.Parameter, optional Q-independent constant background added to all model values. This is turned into a Parameter during the construction of this object. name : str, optional Name of the Model dq : float or refnx.analysis.Parameter, optional - `dq == 0` then no resolution smearing is employed. - `dq` is a float or refnx.analysis.Parameter a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. This value is turned into a Parameter during the construction of this object. threads: int, optional Specifies the number of threads for parallel calculation. This option is only applicable if you are using the ``_creflect`` module. The option is ignored if using the pure python calculator, ``_reflect``. If `threads == -1` then all available processors are used. quad_order: int, optional the order of the Gaussian quadrature polynomial for doing the resolution smearing. default = 17. Don't choose less than 13. If quad_order == 'ultimate' then adaptive quadrature is used. Adaptive quadrature will always work, but takes a _long_ time (2 or 3 orders of magnitude longer). Fixed quadrature will always take a lot less time. BUT it won't necessarily work across all samples. For example, 13 points may be fine for a thin layer, but will be atrocious at describing a multilayer with bragg peaks. dq_type: {'pointwise', 'constant'}, optional Chooses whether pointwise or constant dQ/Q resolution smearing (see `dq` keyword) is used. To use pointwise smearing the `x_err` keyword provided to `Objective.model` method must be an array, otherwise the smearing falls back to 'constant'. q_offset: float or refnx.analysis.Parameter, optional Compensates for uncertainties in the angle at which the measurement is performed. A positive/negative `q_offset` corresponds to a situation where the measured q values (incident angle) may have been under/over estimated, and has the effect of shifting the calculated model to lower/higher effective q values. """ def __init__( self, structure, scale=1, bkg=1e-7, name="", dq=5.0, threads=-1, quad_order=17, dq_type="pointwise", q_offset=0, ): self.name = name self._parameters = None self.threads = threads self.quad_order = quad_order # to make it more like a refnx.analysis.Model self.fitfunc = None # all reflectometry models need a scale factor and background self._scale = possibly_create_parameter(scale, name="scale") self._bkg = possibly_create_parameter(bkg, name="bkg") # we can optimize the resolution (but this is always overridden by # x_err if supplied. There is therefore possibly no dependence on it. self._dq = possibly_create_parameter(dq, name="dq - resolution") self.dq_type = dq_type self._q_offset = possibly_create_parameter(q_offset, name="q_offset") self._structure = None self.structure = structure def __call__(self, x, p=None, x_err=None): r""" Calculate the generative model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ return self.model(x, p=p, x_err=x_err) def __repr__(self): return ( f"ReflectModel({self._structure!r}, name={self.name!r}," f" scale={self.scale!r}, bkg={self.bkg!r}," f" dq={self.dq!r}, threads={self.threads}," f" quad_order={self.quad_order!r}, dq_type={self.dq_type!r}," f" q_offset={self.q_offset!r})" ) @property def dq(self): r""" :class:`refnx.analysis.Parameter` - `dq.value == 0` no resolution smearing is employed. - `dq.value > 0` a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. However, if `x_err` is supplied to the `model` method, then that overrides any setting reported here. """ return self._dq @dq.setter def dq(self, value): self._dq.value = value @property def scale(self): r""" :class:`refnx.analysis.Parameter` - all model values are multiplied by this value before the background is added. """ return self._scale @scale.setter def scale(self, value): self._scale.value = value @property def q_offset(self): r""" :class:`refnx.analysis.Parameter` - compensates for any angular misalignment during an experiment. """ return self._q_offset @q_offset.setter def q_offset(self, value): self._q_offset.value = value @property def bkg(self): r""" :class:`refnx.analysis.Parameter` - linear background added to all model values. """ return self._bkg @bkg.setter def bkg(self, value): self._bkg.value = value def model(self, x, p=None, x_err=None): r""" Calculate the reflectivity of this model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ if p is not None: self.parameters.pvals = np.array(p) if x_err is None or self.dq_type == "constant": # fallback to what this object was constructed with x_err = float(self.dq) return reflectivity( x + self.q_offset.value, self.structure.slabs()[..., :4], scale=self.scale.value, bkg=self.bkg.value, dq=x_err, threads=self.threads, quad_order=self.quad_order, ) def logp(self): r""" Additional log-probability terms for the reflectivity model. Do not include log-probability terms for model parameters, these are automatically included elsewhere. Returns ------- logp : float log-probability of structure. """ return self.structure.logp() @property def structure(self): r""" :class:`refnx.reflect.Structure` - object describing the interface of a reflectometry sample. """ return self._structure @structure.setter def structure(self, structure): self._structure = structure p = Parameters(name="instrument parameters") p.extend([self.scale, self.bkg, self.dq, self.q_offset]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters]) @property def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ self.structure = self._structure return self._parameters
class RI(Scatterer): """ Object representing a materials wavelength-dependent refractive index. A concern is how it needs to be linked to a model. This is to get around a major rewrite of refnx, but isn't the most elegant system. Another issue is that optical parameters are supplied in units of micro meters ('cause thats what seems to be used in refractive index repos and cauchy models), the wavelength of the incident radiation is supplied in nanometers (thats typical) and the fitting is done in angstroms. Very unpleasent. Parameters ---------- value : tuple, string Scattering length density of a material. Units (10**-6 Angstrom**-2) A : float or parameter Cauchy parameter A. If not none RI will use the cauchy model. Default None. B : float or parameter Cauchy parameter B in um^2. Default 0. C : float or parameter Cauchy parameter C in um^4. Default 0. name : str, optional Name of material. Notes ----- An SLD object can be used to create a Slab: >>> # an SLD object representing Silicon Dioxide >>> sio2 = SLD(3.47, name='SiO2') >>> # create a Slab of SiO2 20 A in thickness, with a 3 A roughness >>> sio2_layer = sio2(20, 3) The SLD object can also be made from a complex number, or from Parameters >>> sio2 = SLD(3.47+0.01j) >>> re = Parameter(3.47) >>> im = Parameter(0.01) >>> sio2 = SLD(re) >>> sio2 = SLD([re, im]) """ def __init__(self, value=None, A=None, B=0, C=0, name=""): if (type(value) is str and name == ""): # if there is no name get it from the path name = os.path.basename(value).split(".")[0] super(RI, self).__init__(name=name) assert np.logical_xor( value is None, A is None), "Supply either values or cauchy parameters" if value is not None: if type(value) is str: try: self._wav, self._RI, self._EC = np.loadtxt( value, skiprows=1, delimiter=",", encoding="utf8").T except ValueError: self._wav, self._RI = np.loadtxt( value, skiprows=1, delimiter=",", usecols=[0, 1], encoding="utf8", ).T self._EC = np.zeros_like(self._wav) elif len(value) == 2: self._RI, self._EC = value self._wav = None elif len(value) == 3: self._wav, self._RI, self._EC = value else: raise TypeError("format not recognised") # convert wavelength from um to nm self._wav = self._wav * 1000 else: self._wav = None self._RI = None self._EC = None self.model = None self.set_wav = None self._default_wav = 658 self._parameters = Parameters(name=name) if A is not None: self.A = possibly_create_parameter(A, name=f"{name} - cauchy A") self.B = possibly_create_parameter(B, name=f"{name} - cauchy B") self.C = possibly_create_parameter(C, name=f"{name} - cauchy C") self._parameters.extend([self.A, self.B, self.C]) # The RI needs access to the model to calculate the refractive index. # Can't think of a better way of doing this # reflect_modelSE is going to auto-link this when its called. @property def real(self): """Refractive index, n.""" if self.model is not None: wavelength = self.model.wav elif self.set_wav is not None: wavelength = self.set_wav else: wavelength = self._default_wav warnings.warn("Using default wavelength (model not linked)") if np.any(self._wav): # TODO - raise a warning if the wavelength supplied is outside the # wavelength range covered by the data file. return Parameter(np.interp(wavelength, self._wav, self._RI)) elif self.A is not None: return Parameter(self.A.value + (self.B.value * 1000**2) / (wavelength**2) + (self.C.value**1000**4) / (wavelength**4)) else: return Parameter(value=self._RI) @property def imag(self, wavelength=None): """Extinction coefficent, k.""" if self.model is not None: wavelength = self.model.wav elif self.set_wav is not None: wavelength = self.set_wav else: wavelength = self._default_wav warnings.warn("Using default wavelength (model not linked)") if np.any(self._wav): # TODO - raise a warning if the wavelength supplied is outside the # wavelength range covered by the data file. return Parameter(np.interp(wavelength, self._wav, self._EC)) elif self.A is not None: return Parameter(0) else: return Parameter(value=self._EC) @property def parameters(self): return self._parameters def __repr__(self): return str(f"n: {self.real.value}, k: {self.imag.value}") def __complex__(self): sldc = complex(self.real.value, self.imag.value) return sldc
class ReflectModel(object): r""" Parameters ---------- structure : refnx.reflect.Structure The interfacial structure. scale : float or refnx.analysis.Parameter, optional scale factor. All model values are multiplied by this value before the background is added. This is turned into a Parameter during the construction of this object. bkg : float or refnx.analysis.Parameter, optional Q-independent constant background added to all model values. This is turned into a Parameter during the construction of this object. name : str, optional Name of the Model dq : float or refnx.analysis.Parameter, optional - `dq == 0` then no resolution smearing is employed. - `dq` is a float or refnx.analysis.Parameter a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. However, if `x_err` is supplied to the `model` method, then that overrides any setting given here. This value is turned into a Parameter during the construction of this object. threads: int, optional Specifies the number of threads for parallel calculation. This option is only applicable if you are using the ``_creflect`` module. The option is ignored if using the pure python calculator, ``_reflect``. If `threads == 0` then all available processors are used. quad_order: int, optional the order of the Gaussian quadrature polynomial for doing the resolution smearing. default = 17. Don't choose less than 13. If quad_order == 'ultimate' then adaptive quadrature is used. Adaptive quadrature will always work, but takes a _long_ time (2 or 3 orders of magnitude longer). Fixed quadrature will always take a lot less time. BUT it won't necessarily work across all samples. For example, 13 points may be fine for a thin layer, but will be atrocious at describing a multilayer with bragg peaks. """ def __init__(self, structure, scale=1, bkg=1e-7, name='', dq=5., threads=0, quad_order=17): self.name = name self._parameters = None self.threads = threads self.quad_order = quad_order # to make it more like a refnx.analysis.Model self.fitfunc = None # all reflectometry models need a scale factor and background self._scale = possibly_create_parameter(scale, name='scale') self._bkg = possibly_create_parameter(bkg, name='bkg') # we can optimize the resolution (but this is always overridden by # x_err if supplied. There is therefore possibly no dependence on it. self._dq = possibly_create_parameter(dq, name='dq - resolution') self._structure = None self.structure = structure def __call__(self, x, p=None, x_err=None): r""" Calculate the generative model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ return self.model(x, p=p, x_err=x_err) @property def dq(self): r""" :class:`refnx.analysis.Parameter` - `dq.value == 0` no resolution smearing is employed. - `dq.value > 0` a constant dQ/Q resolution smearing is employed. For 5% resolution smearing supply 5. However, if `x_err` is supplied to the `model` method, then that overrides any setting reported here. """ return self._dq @dq.setter def dq(self, value): self._dq.value = value @property def scale(self): r""" :class:`refnx.analysis.Parameter` - all model values are multiplied by this value before the background is added. """ return self._scale @scale.setter def scale(self, value): self._scale.value = value @property def bkg(self): r""" :class:`refnx.analysis.Parameter` - linear background added to all model values. """ return self._bkg @bkg.setter def bkg(self, value): self._bkg.value = value def model(self, x, p=None, x_err=None): r""" Calculate the reflectivity of this model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ if p is not None: self.parameters.pvals = np.array(p) if x_err is None: # fallback to what this object was constructed with x_err = float(self.dq) return reflectivity(x, self.structure.slabs[..., :4], scale=self.scale.value, bkg=self.bkg.value, dq=x_err, threads=self.threads, quad_order=self.quad_order) def logp(self): r""" Additional log-probability terms for the reflectivity model. Do not include log-probability terms for model parameters, these are automatically included elsewhere. Returns ------- logp : float log-probability of structure. """ return self.structure.logp() @property def structure(self): r""" :class:`refnx.reflect.Structure` - object describing the interface of a reflectometry sample. """ return self._structure @structure.setter def structure(self, structure): self._structure = structure p = Parameters(name='instrument parameters') p.extend([self.scale, self.bkg, self.dq]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters]) @property def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ self.structure = self._structure return self._parameters
class ReflectModelSE(object): r""" Parameters ---------- structure : refnx.reflect.Structure The interfacial structure. name : str, optional Name of the Model """ def __init__( self, structure, wavelength, delta_offset=0, name=None, ): self.name = name self.DeltaOffset = delta_offset self._parameters = None self._flip_delta = False # to make it more like a refnx.analysis.Model self.fitfunc = None # all reflectometry models need a scale factor and background self._wav = possibly_create_parameter(wavelength, name="wavelength") self._structure = None self.structure = structure self.DeltaOffset = possibly_create_parameter(delta_offset, name="delta offset") # THIS IS REALLY QUENSTIONABLE for x in self._structure: try: x.sld.model = self except AttributeError: print("it appears you are using SLD's instead of RIs") def __call__(self, aoi, p=None): r""" Calculate the generative model Parameters ---------- x : float or np.ndarray q values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model x_err : np.ndarray dq resolution smearing values for the dataset being considered. Returns ------- reflectivity : np.ndarray Calculated reflectivity """ return self.model(aoi, p=p) def __repr__(self): return (f"ReflectModel({self._structure!r}, name={self.name!r}," f" wavelength={self.wav!r}," f" delta_offset = {self.delOffset!r} ") @property def wav(self): r""" :class:`refnx.analysis.Parameter` - all model values are multiplied by this value before the background is added. """ return self._wav @wav.setter def wav(self, value): self._wav.value = value @property def delOffset(self): """ :class:`refnx.analysis.Parameter` - the calculated delta offset specific to the ellipsometer and experimental setup used. """ return self.DeltaOffset @delOffset.setter def delOffset(self, value): self.DeltaOffset.value = value def model(self, aoi, p=None): r""" Calculate the reflectivity of this model Parameters ---------- aoi : float or np.ndarray aoi values for the calculation. p : refnx.analysis.Parameters, optional parameters required to calculate the model Returns ------- reflectivity : np.ndarray Calculated reflectivity """ if p is not None: self.parameters.pvals = np.array(p) return Delta_Psi_TMM( AOI=aoi, layers=self.structure.slabs()[..., :4], wavelength=self.wav.value, delta_offset=self.delOffset.value, reflect_delta=self._flip_delta, ) def logp(self): r""" Additional log-probability terms for the reflectivity model. Do not include log-probability terms for model parameters, these are automatically included elsewhere. Returns ------- logp : float log-probability of structure. """ return self.structure.logp() @property def structure(self): r""" :class:`refnx.reflect.Structure` - object describing the interface of a reflectometry sample. """ return self._structure @structure.setter def structure(self, structure): self._structure = structure p = Parameters(name="instrument parameters") p.extend([self.wav, self.delOffset]) self._parameters = Parameters(name=self.name) self._parameters.extend([p, structure.parameters]) @property def parameters(self): r""" :class:`refnx.analysis.Parameters` - parameters associated with this model. """ self.structure = self._structure return self._parameters