Source code for refl1d.stajconvert

# This program is in the public domain
# Author: Paul Kienzle
"""
Convert staj files to Refl1D models
"""
import os

import numpy as np
from numpy import tan, cos, sqrt, radians, degrees, pi
from bumps import parameter

from .staj import MlayerModel, MlayerMagnetic, ERF_FWHM
from .model import Slab, Stack, Repeat
from .magnetism import Magnetism
from .material import SLD
from .resolution import QL2T, sigma2FWHM
from .probe import NeutronProbe, XrayProbe, PolarizedNeutronProbe

[docs] def load_mlayer(filename, fit_pmp=0, name=None, layers=None): """ Load a staj file as a model. """ if filename.endswith('.staj'): staj = MlayerModel.load(filename) model = mlayer_to_model(staj, name=name, layers=layers) else: sta = MlayerMagnetic.load(filename) model = mlayer_magnetic_to_model(sta, name=name, layers=layers) if fit_pmp != 0: fit_all(model, pmp=fit_pmp) return model
[docs] def save_mlayer(experiment, filename, datafile=None): """ Save a model to a staj file. """ staj = model_to_mlayer(experiment, datafile) #print staj staj.save(filename)
[docs] def fit_all(M, pmp=20): """ Set all non-zero parameters to fitted parameters inside the model. """ # Exclude unlikely fitting parameters exclude = set((M.sample[0].thickness, M.sample[-1].thickness, M.sample[-1].interface, M.probe.back_absorption, )) if M.probe.intensity.value == 1: exclude.add(M.probe.intensity) if M.probe.background.value < 2e-10: exclude.add(M.probe.background.value) # Fit everything else using a range of +/- pmp % for p in parameter.unique(M.parameters()): if p in exclude: continue if p.value != 0: p.pmp(pmp)
#p.fixed = False
[docs] def mlayer_to_model(staj, name=None, layers=None): """ Convert a loaded staj file to a refl1d experiment. Returns a new experiment """ from .experiment import Experiment sample = _mlayer_to_stack(staj, name, layers) probe = _load_probe(staj, name, xs='') return Experiment(sample=sample, probe=probe)
def _mlayer_to_stack(s, name, layers): """ Return a sample stack based on the model used in the staj file. """ # check pars if layers and len(layers) != len(s.rho): raise ValueError("Have %d staj layers but got %d layer names" % (len(s.rho), len(layers))) # prepend datafile name to layers if name is None: name = os.path.splitext(s.data_file)[0] if name and not name.endswith(" "): name += " " i1 = s.num_top+1 i2 = s.num_top+s.num_middle+1 # Construct slabs slabs = [] for i in reversed(range(len(s.rho))): if layers: Lname = layers[len(layers)-i-1] elif i == 0: Lname = 'V' elif i < i1: Lname = 'T%d'%(i) elif i < i2: Lname = 'M%d'%(i-i1+1) else: Lname = 'B%d'%(i-i2+1) slabs.append(Slab(material=SLD(rho=s.rho[i], irho=s.irho[i], name=name+Lname), thickness=s.thickness[i], interface=s.sigma_roughness[i])) # Build stack if s.num_repeats == 0: stack = Stack(slabs[:i1]+slabs[i2:]) elif s.num_repeats == 1: stack = Stack(slabs) else: stack = Stack(slabs[:i1]+[Repeat(Stack(slabs[i1:i2]))]+slabs[i2:]) return stack XS = {'A': '--', 'B': '-+', 'C': '+-', 'D': '++', '':''} def _load_probe(s, name, xs): if name is None: name = os.path.splitext(s.data_file)[0] if s.data_file == "": filename = "simulated" Q = np.linspace(s.Qmin, s.Qmax, s.num_Q) R, dR = None, None else: filename = s.data_file Q, R, dR = np.loadtxt(s.data_file+xs).T # Use Q and wavelength L from the staj file to determine angle T L = s.wavelength T = QL2T(Q=Q, L=s.wavelength) # Refl1D uses the following for dQ: # # (dQ/Q)^2 = (dL/L)^2 + (dT/tan(T))^2 # # Given the mlayer resolution dQ, and wavelength divergence dL, we can solve the # above for dT such that dQ in refl1d matches dQ in mlayer. # # dT = sqrt( tan(T)^2 * ( (dQ/Q)^2 - (dL/L)^2) ) ) # = sqrt( (dQ*L/4*pi*sin(T))^2*(sin(T)/cos(T))^2 - (tan(T)*dL/L)^2 ) # = sqrt( (dQ*L/4*pi*cos(T))^2 - (tan(T)*dL/L)^2 ) dQ = s.FWHMresolution(Q) dL = s.wavelength_dispersion dT = degrees(sqrt((dQ*L/(4*pi*cos(radians(T))))**2 - (tan(radians(T))*dL/L)**2)) # Hack: X-ray is 1.54; anything above 2 is neutron. Doesn't matter since # materials are set as SLD rather than composition. if s.wavelength < 2: probe = XrayProbe else: probe = NeutronProbe probe = probe(T=T, dT=dT, L=L, dL=dL, data=(R, dR), theta_offset=getattr(s, 'theta_offset', 0), # gj2 has no theta offset background=s.background, intensity=s.intensity, name=name) probe.filename = filename #probe.oversample(n=10) return probe
[docs] def model_to_mlayer(model, datafile): """ Return an mlayer model based on the a slab stack. Raises TypeError if model cannot be stored as a staj file. """ stack = model.sample probe = model.probe staj = MlayerModel(roughness_steps=51) # Set up beam info if (probe.L != probe.L[0]).any(): # Reason is that mlayer uses mu/(2 lambda) rather than irho raise TypeError("Mlayer only supports monochromatic sources") staj.set(wavelength=probe.L[0], intensity=probe.intensity.value, background=probe.background.value, theta_offset=probe.theta_offset.value) if datafile: staj.data_file = os.path.basename(datafile) else: staj.Qmin, staj.Qmax = min(probe.Qo), max(probe.Qo) staj.num_Q = len(probe.Qo) staj.fit_FWHMresolution(probe.Qo, sigma2FWHM(probe.dQ)) # Interpret slabs and repeats sections = [] section = [] repeats = 0 for l in stack: if isinstance(l, Slab): section.append(l) elif isinstance(l, Repeat): sections.append(section) sections.append(l[:]) repeats = l.repeat section = [] else: raise TypeError("Only slabs supported") sections.append(section) if len(sections) > 3: raise TypeError("Only one repeated section supported") if len(sections) == 3: for l in sections[1]: if not isinstance(l, Slab): raise TypeError("Only slabs supported in repeat section") num_top = len(sections[2]) - 1 num_middle = len(sections[1]) num_bottom = len(sections[0]) if num_top > 9 or num_middle > 9 or num_bottom > 9: raise TypeError("Maximum section length of 9") if num_top < 1 or num_middle < 1 or num_bottom < 1: raise TypeError("Need at least one slab per section, plus vacuum") staj.num_top = num_top staj.num_middle = num_middle staj.num_bottom = num_bottom staj.num_repeats = repeats slabs = [] slabs.extend(reversed(sections[2])) slabs.extend(reversed(sections[1])) slabs.extend(reversed(sections[0])) else: # must be only one section slabs = list(reversed(sections[0])) if len(slabs) > 28: raise TypeError("Too many slabs (only 28 slabs allowed)") # Convert slabs to sld parameters #print "\n".join(str(s) for s in slabs) values = [] for layer in slabs: rho, irho = layer.material.sld(probe) thickness = layer.thickness.value roughness = layer.interface.value values.append((rho, irho, thickness, roughness)) vectors = [np.array(v) for v in zip(*values)] # If back reflectivity, reverse the layers and move the interfaces # from the top of the layer to the bottom. if probe.back_reflectivity: vectors = [v[::-1] for v in vectors] vectors[3] = np.roll(vectors[3], 1) staj.num_top, staj.num_bottom = staj.num_bottom, staj.num_top staj.rho, staj.irho, staj.thickness, staj.sigma_roughness = vectors # If no repeats, split the model into sections if len(sections) == 1: # If the stack is too short, add layers at the top until it is # tall enough. These layers should have the same SLD as the # old top layer, and a thickness large enough to accommodate # the interface between the top layer and the second layer. while len(staj.rho) < 4: staj.rho = np.hstack((staj.rho[0], staj.rho)) staj.irho = np.hstack((staj.irho[0], staj.irho)) staj.thickness = np.hstack((0, 3.5*staj.sigma_roughness[1], staj.thickness[1:])) staj.sigma_roughness = np.hstack((0, staj.sigma_roughness)) staj.split_sections() return staj
[docs] def mlayer_magnetic_to_model(sta, name=None, layers=None): """ Convert a loaded sta file to a refl1d experiment. Returns a new experiment """ from .experiment import Experiment sample = _mlayer_magnetic_to_stack(sta, name, layers) probe = _mlayer_magnetic_to_probe(sta, name) return Experiment(sample=sample, probe=probe, dz=0.1)
def _mlayer_magnetic_to_stack(s, name, layers): """ Return a sample stack based on the model used in the sta file. """ # check pars if layers and len(layers) != len(s.rho): raise ValueError("Have %d sta layers but got %d layer names" % (len(s.rho), len(layers))) # prepend datafile name to layers if name is None: name = os.path.splitext(s.data_file)[0] if name and not name.endswith(" "): name += " " # Construct slabs magnetic_offset = np.cumsum(s.thickness-s.mthickness) slabs = [] nlayers = len(s.rho) for i in range(nlayers-1, -1, -1): if layers: Lname = layers[len(layers)-i-1] elif i == 0: Lname = 'V' else: Lname = 'M%d'%i slab_i = Slab(material=SLD(rho=s.rho[i], irho=s.irho[i], name=name+Lname), thickness=s.thickness[i], interface=s.sigma_roughness[i]) if s.mrho[i] != 0.0: slab_i.magnetism = Magnetism(s.mrho[i], s.mtheta[i], interface_below=s.sigma_mroughness[i+1] if i < nlayers-1 else 0, interface_above=s.sigma_mroughness[i], dead_below=magnetic_offset[i+1] if i < nlayers-1 else 0, dead_above=magnetic_offset[i]) slabs.append(slab_i) return Stack(slabs) def _mlayer_magnetic_to_probe(s, name): """ Return a model probe based on the data used for the staj file. """ if name is None: name = os.path.splitext(s.data_file)[0] active_xsec = s.active_xsec.upper() xs = [_load_probe(s, name, xs) if (xs in active_xsec) else None for xs in 'ABCD'] probe = PolarizedNeutronProbe(xs, Aguide=s.guide_angle) #probe.oversample(n=6) return probe