Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge FreeMagnetismInterface feature enhancement #232

Open
wants to merge 66 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
acc2954
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
92a70ee
FIX: length of vectors not equal when dthetaM is not defined
acaruana2009 Jan 16, 2021
9c88dcb
Merge pull request #101 from acaruana2009/magnetism-interface
bmaranville Mar 11, 2021
a243cf0
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
a60f384
Lint
acaruana2009 Mar 16, 2021
0e3c6d5
FIX: nans in mono.py as result of divide by zero
acaruana2009 Mar 24, 2021
ad869e8
FIX: changed FreeMagnetismInterface thetaM above or below default val…
acaruana2009 Mar 24, 2021
be0a13c
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
155b1aa
Merge pull request #147 from reflectometry/magnetism-interface
acaruana2009 Jan 20, 2022
bb5edf9
create self-extracting version of unstable windows .exe.zip
bmaranville Nov 30, 2021
31e462a
numba required for readthedocs build
pkienzle Nov 30, 2021
7258b39
set max initial ylim on spin asymmetry plot
bmaranville Dec 14, 2021
3da1e10
add link to unstable release page
bmaranville Dec 14, 2021
c64268e
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
a9b4c6f
FIX: length of vectors not equal when dthetaM is not defined
acaruana2009 Jan 16, 2021
e060d04
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
7441f89
Lint
acaruana2009 Mar 16, 2021
145d624
FIX: nans in mono.py as result of divide by zero
acaruana2009 Mar 24, 2021
3db9b1f
FIX: changed FreeMagnetismInterface thetaM above or below default val…
acaruana2009 Mar 24, 2021
1c041d3
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
22029f8
Merge branch 'magnetism-interface-AJC' of https://github.com/reflecto…
acaruana2009 Jun 9, 2023
b9fd580
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
fc1799f
FIX: length of vectors not equal when dthetaM is not defined
acaruana2009 Jan 16, 2021
67739a2
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
20037d7
Lint
acaruana2009 Mar 16, 2021
f962e6f
FIX: nans in mono.py as result of divide by zero
acaruana2009 Mar 24, 2021
9ec9eb9
FIX: changed FreeMagnetismInterface thetaM above or below default val…
acaruana2009 Mar 24, 2021
5449100
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
d61f1e8
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
60fd5e3
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
8cb9239
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
717af74
Merge remote-tracking branch 'origin/magnetism-interface-AJC' into ma…
acaruana2009 Sep 10, 2024
73231d4
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
1422661
FIX: length of vectors not equal when dthetaM is not defined
acaruana2009 Jan 16, 2021
87b5f66
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
34bec09
Lint
acaruana2009 Mar 16, 2021
39d654d
FIX: nans in mono.py as result of divide by zero
acaruana2009 Mar 24, 2021
b2201e8
FIX: changed FreeMagnetismInterface thetaM above or below default val…
acaruana2009 Mar 24, 2021
12acec5
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
a92bd1b
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
6beb4f4
FIX: Make thetaM behave the same as rhoM
acaruana2009 Mar 11, 2021
40c5b96
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
f0c66be
ENH: add free interface in addition to free layer for magnetism.
Nov 17, 2020
6d0833f
FIX: length of vectors not equal when dthetaM is not defined
acaruana2009 Jan 16, 2021
197cc95
FIX: changed FreeMagnetismInterface thetaM above or below default val…
acaruana2009 Mar 24, 2021
b548ded
Additional Edits for FreeMagnetismInterface
acaruana2009 Mar 26, 2021
231ebb7
Merge remote-tracking branch 'origin/magnetism-interface-AJC' into ma…
acaruana2009 Nov 11, 2024
0d7e3ad
Merge pull request #1 from acaruana2009/magnetism-interface-AJC
acaruana2009 Nov 12, 2024
f7b423c
Linting and bringing in line with master
acaruana2009 Nov 12, 2024
b6d57f7
Maint: Serialize `FreeMagnetismInterface`
acaruana2009 Nov 12, 2024
8158209
Tidying up to minimise changes
acaruana2009 Nov 12, 2024
410a858
Fix: Serializing/De-serializing of FreeInterface
acaruana2009 Nov 21, 2024
84153e3
Tidy up imports
acaruana2009 Nov 21, 2024
f5d41cb
Sorting Merge conflicts
acaruana2009 Nov 21, 2024
62ce7cb
Further import tidy up
acaruana2009 Nov 21, 2024
cf702e8
Merge branch 'master' into magnetism-interface-AJC-webview
acaruana2009 Nov 21, 2024
66e354b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 21, 2024
6aa2d2e
Updated dataclass types for `FreeMagnetismInterface`
acaruana2009 Nov 21, 2024
4f50fbc
minor fixup on stored attribute types (they are always stored as Para…
bmaranville Dec 13, 2024
8c1de01
fixes to FreeInterface class, adding magnetism and setting last eleme…
bmaranville Dec 16, 2024
7aa06d0
add FreeMagnetismInterface class
bmaranville Dec 16, 2024
689653a
add FreeMagnetismInterface to names
bmaranville Dec 16, 2024
2a65ec0
Merge remote-tracking branch 'origin/magnetism-interface-AJC-webview'…
acaruana2009 Dec 17, 2024
25a0be0
FIX: Limits of rhoM should be +- inf in `FreeMagnetism`
acaruana2009 Dec 17, 2024
ab5ca3e
ENH: add magnetism attr to `FreeLayer`
acaruana2009 Dec 17, 2024
a5e9d34
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 148 additions & 1 deletion refl1d/magnetism.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"""

from dataclasses import dataclass
from typing import Dict, List, Literal, Union
from typing import Optional, Any, Union, Dict, List, Literal

import numpy as np
from bumps.mono import monospline
Expand Down Expand Up @@ -437,3 +437,150 @@ def __str__(self):

def __repr__(self):
return "FreeMagnetism"


@dataclass(init=False)
class FreeMagnetismInterface(BaseMagnetism):
r"""
Spline change in magnetism throughout layer.

Defines monotonic splines for rhoM and thetaM with shared knot positions.

*dz* is the relative $z$ step between the knots, with position $z_k$
defined by $z_k = w \sum_{i=0}^k \delta z_i / \sum_{i=0}^n \delta z_i$,
where $n$ is the number of intervals. The resulting $z$ must be monotonic,
with $\delta z_i \ge 0$ for all intervals.

*drhoM* gives the relative $\rho_M$ step between knots. Unlike
$\rho_{Mk} = \sum_{i=0}^k \delta \rho_{Mi} / \sum_{i=0}^n \delta \delta \rho_{Mi}$.

*dthetaM* gives the magnetic angle for each knot.

*name* is the base name for the various layer parameters.

*dead_above* and *dead_below* define magnetically dead layers at the
nuclear boundaries. These can be negative if magnetism extends beyond
the nuclear boundary.

*interface_above* and *interface_below* define the magnetic interface
at the boundaries, if it is different from the nuclear interface.

*mbelow* and *mabove* are the rhoM parameter values of
the above and below layers respectively. Do not specify if
layers either side are not magnetic.

*tbelow* and *tabove* are the thetaM parameter values of
the above and below layers respectively. Do not specify if
layers either side are not magnetic.
"""

name: Optional[str]
mbelow: Optional[Union[Parameter, float]]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bmaranville I am not sure if this is the current style that you are using for typing. I.e. would you prefer to remove Any and Optional?

Copy link
Member

@bmaranville bmaranville Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really a question of style - the dataclass attribute types should be exactly what will be stored in those attributes after init + post_init. There can be different types for the arguments of the init function, if they are going to be coerced before being stored in the attribute. For example, an argument will often be of type Union[float, Parameter] but then we store it as Parameter.default(value) which coerces it to a Parameter, so Parameter should be the type of the attribute (not the Union)

Copy link
Contributor Author

@acaruana2009 acaruana2009 Nov 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK so in this case, we could drop the Optional since we probably don't want None as one of the types? That said tabove and tbelow do take None as the default arg.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typing of the arg is not the same as the typing of the attribute - tbelow and tabove are both initialized as Parameter objects during init, even if they are None as an argument. The type of the attributes should be Parameter

mabove: Optional[Union[Parameter, float]]
tbelow: Optional[Any]
tabove: Optional[Any]
dz: List[Union[float, Parameter]]
drhoM: List[Union[float, Parameter]]
dthetaM: List[Union[float, Parameter]]

magnetic = True

def __init__(
self, dz=(), drhoM=(), dthetaM=(), mbelow=0, mabove=0, tbelow=None, tabove=None, name="MagInterface", **kw
):
BaseMagnetism.__init__(self, name=name, **kw)

def parvec(vector, name, limits):
return [Parameter.default(p, name=name + "[%d]" % i, limits=limits) for i, p in enumerate(vector)]

self.drhoM, self.dthetaM, self.dz = [
parvec(v, name + " " + part, limits)
for v, part, limits in zip((drhoM, dthetaM, dz), ("drhoM", "dthetaM", "dz"), ((-1, 1), (-1, 1), (0, 1)))
]
self.mbelow = Parameter.default(mbelow, name=name + " mbelow", limits=(-np.inf, np.inf))
self.mabove = Parameter.default(mabove, name=name + " mabove", limits=(-np.inf, np.inf))

# if only tabove or tbelow is defined then they are made equal
# this is to deal with the situation of a non-magnetic
# layer next to a magnetic one
if tabove is None and tbelow is None:
tbelow = tabove = DEFAULT_THETA_M
elif tbelow is None:
tbelow = tabove
elif tabove is None:
tabove = tbelow

self.tbelow = Parameter.default(tbelow, name=name + " tbelow", limits=(0, 360))
self.tabove = Parameter.default(tabove, name=name + " tabove", limits=(0, 360))
if len(self.dz) != len(self.drhoM):
raise ValueError("Need one dz for each drhoM")
if len(self.dthetaM) > 0 and len(self.drhoM) != len(self.dthetaM):
raise ValueError("Need one dthetaM for each drhoM")

def parameters(self):
parameters = BaseMagnetism.parameters(self)
parameters.update(
drhoM=self.drhoM,
dthetaM=self.dthetaM,
dz=self.dz,
mbelow=self.mbelow,
mabove=self.mabove,
tbelow=self.tbelow,
tabove=self.tabove,
)
return parameters

def to_dict(self):
result = BaseMagnetism.to_dict(self)
result["dz"] = to_dict(self.dz)
result["drhoM"] = to_dict(self.drhoM)
result["dthetaM"] = to_dict(self.dthetaM)
result["mbelow"] = to_dict(self.mbelow)
result["mabove"] = to_dict(self.mabove)
result["tbelow"] = to_dict(self.tbelow)
result["tabove"] = to_dict(self.tabove)
return result

def profile(self, Pz, thickness):
z = np.hstack((0, np.cumsum(np.asarray([v.value for v in self.dz], "d"))))
if z[-1] == 0:
z[-1] = 1
z *= thickness / z[-1]

rhoM_fraction = np.hstack((0, np.cumsum(np.asarray([v.value for v in self.drhoM], "d"))))
# AJC added since without the line below FreeMagnetismInterface
# does not initialise properly - fixes strange behaviour at drho=0 on end point
if rhoM_fraction[-1] == 0:
rhoM_fraction[-1] = 1

rhoM_fraction *= 1 / rhoM_fraction[-1]
PrhoM = np.clip(monospline(z, rhoM_fraction, Pz), 0, 1)

if self.dthetaM:
thetaM_fraction = np.hstack((0, np.cumsum(np.asarray([v.value for v in self.dthetaM], "d"))))
if thetaM_fraction[-1] == 0:
thetaM_fraction[-1] = 1

thetaM_fraction *= 1 / thetaM_fraction[-1]
PthetaM = np.clip(monospline(z, thetaM_fraction, Pz), 0, 1)
else:
# AJC changed from len(z) to PrhoM - since PrhoM is the length of the vector
# we want PthetaM to match - otherwise slabs.add_magnetism throws an error
PthetaM = np.linspace(0.0, 1.0, len(PrhoM))

return PrhoM, PthetaM

def render(self, probe, slabs, thickness, anchor, sigma):
Pw, Pz = slabs.microslabs(thickness)
rhoM_profile, thetaM_profile = self.profile(Pz, thickness)
mbelow, mabove = self.mbelow.value, self.mabove.value
tbelow, tabove = self.tbelow.value, self.tabove.value
rhoM = (1 - rhoM_profile) * mbelow + rhoM_profile * mabove
thetaM = (1 - thetaM_profile) * tbelow + thetaM_profile * tabove
slabs.add_magnetism(anchor=anchor, w=Pw, rhoM=rhoM, thetaM=thetaM, sigma=sigma)

def __str__(self):
return "freemagint(%d)" % (len(self.drhoM))

def __repr__(self):
return "FreeMagnetismInterface"
27 changes: 21 additions & 6 deletions refl1d/mono.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from numpy import asarray, clip, cumsum, diff, hstack, inf, sort

from . import util
from .magnetism import BaseMagnetism
from .material import Scatterer
from .model import Layer


Expand Down Expand Up @@ -115,16 +117,19 @@ class FreeInterface(Layer):
with slabs.
"""

name: Optional[str]
below: Optional[Any]
above: Optional[Any]
name: str
below: Scatterer
above: Scatterer
thickness: Par
interface: Par
dz: List[Union[float, Par]]
dp: List[Union[float, Par]]
dz: List[Par]
dp: List[Par]
magnetism: Optional[BaseMagnetism] = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this really be any BaseMagnetism object, or does it have to be specifically a FreeMagnetismInterface?

# inflections: List[Any]

def __init__(self, thickness=0, interface=0, below=None, above=None, dz=None, dp=None, name="Interface"):
def __init__(
self, thickness=0, interface=0, below=None, above=None, dz=None, dp=None, name="Interface", magnetism=None
):
self.name = name
self.below, self.above = below, above
self.thickness = Par.default(thickness, limits=(0, inf), name=name + " thickness")
Expand All @@ -146,6 +151,7 @@ def __init__(self, thickness=0, interface=0, below=None, above=None, dz=None, dp
self.dp = [Par.default(p, name=name + " dp[%d]" % i, limits=(0, inf)) for i, p in enumerate(dp)]
self.inflections = Par(name=name + " inflections")
self.inflections.equals(ParFunction(inflections, dx=self.dz, dy=self.dp))
self.magnetism = magnetism

def parameters(self):
return {
Expand All @@ -156,6 +162,7 @@ def parameters(self):
"below": self.below.parameters(),
"above": self.above.parameters(),
"inflections": self.inflections,
"magnetism": self.magnetism.parameters() if self.magnetism is not None else None,
}

def to_dict(self):
Expand All @@ -175,14 +182,22 @@ def profile(self, Pz):
if p[-1] == 0:
p[-1] = 1
p *= 1 / p[-1]
# AJC included condition as if z[-1] == 0 then z *= thickness/z[-1] == [nan]*len(z)
# This then ends with bumps.mono.Monospline adding an extra element as a result of
# line 42 in bumps.mono.Monospline
if z[-1] == 0:
z[-1] = 1
z *= thickness / z[-1]
profile = clip(monospline(z, p, Pz), 0, 1)
return profile

def render(self, probe, slabs):
thickness = self.thickness.value

# TODO: why is provided if it is ignored?
# interface ignored for FreeInterface
# interface = self.interface.value

below_rho, below_irho = self.below.sld(probe)
above_rho, above_irho = self.above.sld(probe)
# Pz is the center, Pw is the width
Expand Down
2 changes: 1 addition & 1 deletion refl1d/names.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from .flayer import FunctionalMagnetism, FunctionalProfile
from .instrument import Monochromatic, Pulsed
from .magnetic import FreeMagnetic, MagneticSlab, MagneticStack, MagneticTwist
from .magnetism import FreeMagnetism, Magnetism, MagnetismStack, MagnetismTwist
from .magnetism import FreeMagnetism, FreeMagnetismInterface, Magnetism, MagnetismStack, MagnetismTwist
from .material import SLD, Compound, Material, Mixture

# Pull in common materials for reflectometry experiments.
Expand Down