Open sstendahl opened 10 months ago
Just for reference, I've got an implementation right now for the first option (from physical parameters) and the last option (from SLD directly). This looks something like this:
class MagneticLayerSLD(refnx.reflect.structure.Slab):
def __init__(self,
SLDn = 1,
SLDm = 0,
thick = 10,
rough = 6,
underlayer = True,
name="magnetic_layer"):
self.SLD_n = SLDn
self.SLD_m = SLDm
self.thickness = thick
self.roughness = rough
self.underlayer = underlayer
self.name = name
super().__init__(thick=self.thickness, sld=self.SLD_n, rough=self.roughness, name=self.name)
@property
def spin_up(self):
SLD_value = self.SLD_n + self.SLD_m
return SLD(SLD_value, name='spin_up')(thick=self.thickness, rough=self.rough)
@property
def spin_down(self):
SLD_value = self.SLD_n - self.SLD_m
return SLD(SLD_value, name='spin_down')(thick=self.thickness, rough=self.rough)
A list of the magnetic structures can be retrieved using sample.get_structures()
which returns the spin-up and spin-down case:
def get_structures(self):
"""
Get a list of the possible sample structures.
"""
structures = []
spin_up_structure = self.structure.copy()
spin_down_structure = self.structure.copy()
for i, layer in enumerate(self.structure):
if isinstance(layer, MagneticLayer) or isinstance(layer, MagneticLayerSLD):
spin_up_structure[i] = layer.spin_up
spin_down_structure[i] = layer.spin_down
structures.extend([spin_up_structure, spin_down_structure])
return structures
Even if there's no magnetic layer in the sample, it returns a spin-up and a spin-down structure. However, these will be identical, which is exactly as intended, but the name of this method is not ideal. This above method can lead to pretty easy implementation to take both cases into account. For example to get a list of models, qs and counts etc.. to generate a Fisher
object I do the following:
@classmethod
def from_sample(cls,
sample,
angle_times,
contrasts = None,
underlayers = None,
instrument = None):
"""
Get Fisher object using a sample.
Seperate constructor for magnetic simulation maybe? Probably depends
on new simulate function either way.
"""
qs, counts, models = [], [], []
structures = sample.get_structures()
for structure in structures:
model = refnx.reflect.ReflectModel(structure)
sim = SimulateReflectivity(model, angle_times)
data = sim.simulate()
qs.append(data[0])
counts.append(data[3])
models.append(model)
xi = sample.get_varying_parameters()
return cls(qs, xi, counts, models)
So a Fisher
object can just be created like Fisher.from_sample(sample)
. And it creates an object that includes both spin-up and spin-down direction. Some arguments may be nice to specify which spin-states should be included (which in turn would then be parsed into get_structures()
, so that it's still possible to create such an object without the spin states, but again it's a bit WIP right now.
refnx has a refnx.reflect.structure._PolarisedSlab
, and refnx.reflect.structure.Structure
has a private _spin
attribute that can be used to specify whether a Structure
should be considered to be spin up/spin down.
At the moment they're both private as they're considered to be in a prototype phase. Of course, you could copy the _PolarisedSlab
to your code if you wanted to, and monkeypatch Structure
to add the _spin
attribute yourself.
Thought I'd open an issue on this before I forget with the christmas season in sight.
Basically, I've got a working implementation with magnetic layers, but the question mainly remains on how the scattering length densities should be calculated. There's two distinct ways I'm thinking about this:
MagneticLayer(thick = 30, density = 4, mag=3, weight=5)
. And the SLD is calculated based on the provided arguments. This does however require the magnetic moment, the mass density as well as the atomic weight. The same parameters are required for the nuclear SLD, minus the magnetic moment.periodictable
module. This could allow users to insert a material directly, which may be convenient. Especially if you're not optimising the SLDMagneticLayer(thick=30, SLDn=4, SLDm=3)
. This is closer to vanillarefnx
(at least so far, we've been working with SLD directly there), and this allows to optimise for the SLD directly if we want to provide a theoretically best scattering profile.Of course it may also be worthwhile to support multiple versions. The implementation is quite simple, and we can just create a
MagneticLayer
class, and inherit from there for the three above options. This would however require users to import the specific kind of layer they want to work with, but that may not be that terrible.Another option is to dynamically "guess" what the user tries from their input arguments. So with a single class, and if both
SLDn
anddensity
are given for example, raise an error that incompatible arguments are given. But that will likely be much less clean code-wise, and make things less maintainable. So I tend to lean towards having a seperate class (inheriting from aMagneticLayer
class) for each input type.