Skip to content

Commit 9caae97

Browse files
authored
butlerpd
Everything is ready and approved so merging this PR
2 parents 1f94047 + e1f36e5 commit 9caae97

3 files changed

Lines changed: 215 additions & 0 deletions

File tree

sasmodels/models/img/tetrapod.png

28.7 KB
Loading

sasmodels/models/tetrapod.c

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Half of the tetrahedral angle acos(-1/3)/2 ~54.7356 deg.
2+
// Precompute its cosine and sine: cos = sqrt(1/3), sin = sqrt(2/3).
3+
#define COS_HALF_ANGLE 0.57735026918962573 // sqrt(1/3)
4+
#define SIN_HALF_ANGLE 0.81649658092772603 // sqrt(2/3)
5+
6+
static double u_n(int n, double theta, double alpha) {
7+
const double phi[4] = {0.0, M_PI_2, M_PI, 3.0 * M_PI_2};
8+
const double sign[4] = {1.0, -1.0, 1.0, -1.0};
9+
return sign[n] * COS_HALF_ANGLE * cos(theta) +
10+
SIN_HALF_ANGLE * sin(theta) * cos(alpha - phi[n]);
11+
}
12+
13+
// L: arm length, R: core radius, t: shell thickness, R_o = R + t: outer radius
14+
static double Fq_n(double q, double u, double L, double R, double t,
15+
double contrast_core, double contrast_shell) {
16+
double R_o = R + t;
17+
double quL2 = q * u * L * 0.5;
18+
double mu = sqrt(fmax(0.0, 1.0 - u * u));
19+
double V_c = M_PI * R * R * L;
20+
double V_o = M_PI * R_o * R_o * L;
21+
return sas_sinx_x(quL2) * (contrast_core * V_c * sas_2J1x_x(q * mu * R) +
22+
contrast_shell * V_o * sas_2J1x_x(q * mu * R_o));
23+
}
24+
25+
static double form_volume(double L, double R, double t) {
26+
// V = 4 * pi * (R + t)^2 * L (outer volume of 4 arms)
27+
return 4.0 * M_PI * (R + t) * (R + t) * L;
28+
}
29+
30+
static double radius_effective(int mode, double L, double R, double t) {
31+
switch (mode) {
32+
default:
33+
case 1: // equivalent volume sphere
34+
return cbrt(form_volume(L, R, t) / M_4PI_3);
35+
case 2: // length of tetrapod arms (L)
36+
return L;
37+
}
38+
}
39+
40+
static double Iq(double q, double L, double R, double t, double sld_core,
41+
double sld_shell, double sld_solvent) {
42+
double contrast_core = sld_core - sld_shell;
43+
double contrast_shell = sld_shell - sld_solvent;
44+
double total = 0.0;
45+
46+
for (int dtheta = 0; dtheta < GAUSS_N; dtheta++) {
47+
double theta =
48+
M_PI_2 * (GAUSS_Z[dtheta] + 1.0); // map from [-1, 1] to [0, pi]
49+
double w_theta =
50+
GAUSS_W[dtheta] * M_PI_2; // adjust weight for the new range
51+
52+
double integral_alpha = 0.0;
53+
for (int dalpha = 0; dalpha < GAUSS_N; dalpha++) {
54+
double alpha =
55+
M_PI * (GAUSS_Z[dalpha] + 1.0); // map from [-1, 1] to [0, 2*pi]
56+
double w_alpha =
57+
GAUSS_W[dalpha] * M_PI; // adjust weight for the new range
58+
59+
double u[4], F[4];
60+
for (int n = 0; n < 4; n++) {
61+
u[n] = u_n(n, theta, alpha);
62+
F[n] = Fq_n(q, u[n], L, R, t, contrast_core, contrast_shell);
63+
}
64+
double sum_arms = 0.0;
65+
for (int n = 0; n < 4; n++) {
66+
sum_arms += F[n] * F[n];
67+
for (int m = n + 1; m < 4; m++) {
68+
sum_arms += 2.0 * F[n] * F[m] * cos(q * (u[n] - u[m]) * L / 2.0);
69+
}
70+
}
71+
sum_arms *= sin(theta);
72+
integral_alpha += sum_arms * w_alpha;
73+
}
74+
total += integral_alpha * w_theta;
75+
}
76+
return 1e-4 * total / (4.0 * M_PI);
77+
}

sasmodels/models/tetrapod.py

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
r"""
2+
Definition
3+
----------
4+
5+
Calculates the scattering from a tetrapod-shaped structure. A tetrapod consists
6+
of four cylindrical arms radiating from a central point, oriented along the
7+
(1,1,1), (-1,-1,1), (-1,1,-1), and (1,-1,-1) directions.
8+
9+
.. figure:: img/tetrapod.png
10+
11+
Tetrapod schematic. Each of the four arms is a core--shell cylinder of
12+
length $L$, core radius $R$ and shell thickness $t$;
13+
the arm cross-section is shown in the inset. The arms radiate from a
14+
central junction along tetrahedral directions.
15+
16+
The scattering intensity is calculated as an average over all
17+
orientations:
18+
19+
.. math::
20+
21+
I(q) = \frac{(\Delta \rho)^2}{4\pi}
22+
\int_0^{\pi} \int_0^{2\pi}
23+
\left|\sum_{n=1}^{4} F_n(q, \theta, \varphi)\right|^2
24+
\sin\theta \, d\theta \, d\varphi
25+
26+
where $F_n$ is the core-shell form factor amplitude of the $n$-th arm:
27+
28+
.. math::
29+
30+
F_n(q, \theta, \varphi) = \text{sinc}\!\left(\frac{q u_n L}{2}\right)
31+
\left[
32+
(\rho_\text{core} - \rho_\text{shell})\, V_\text{core}\,
33+
\frac{2 J_1(q \mu_n R)}{q \mu_n R}
34+
+ (\rho_\text{shell} - \rho_\text{solvent})\, V_\text{outer}\,
35+
\frac{2 J_1(q \mu_n (R+t))}{q \mu_n (R+t)}
36+
\right]
37+
38+
with $u_n = \hat{q} \cdot \hat{a}_n$, $\mu_n = \sqrt{1 - u_n^2}$, $L$ the arm
39+
length, $R$ the arm core radius, $R + t$ the outer radius ($t$ = shell
40+
thickness), and $V_\text{core} = \pi R^2 L$, $V_\text{outer} = \pi (R+t)^2 L$.
41+
Expanding the squared modulus into a double sum gives:
42+
43+
.. math::
44+
45+
I(q) = \frac{1}{4\pi}
46+
\int \sum_{n=1}^{4}\sum_{m=1}^{4}
47+
F_n F_m \cos\!\left(\frac{q(u_n-u_m)L}{2}\right)
48+
\sin\theta \, d\theta \, d\varphi
49+
50+
The cosine factor is the interference term between the centres of arms $n$
51+
and $m$, which are displaced by $\tfrac{L}{2}\hat{a}_n$ from the junction.
52+
53+
Geometry
54+
--------
55+
56+
The four arms are oriented along tetrahedral directions. With
57+
$A = 109.5 /2$ (the half-angle between arms), the arm unit vectors and the
58+
corresponding projections $u_n$ are
59+
60+
.. math::
61+
62+
u_n = s_n \cos A \cos\theta + \sin A \sin\theta \cos(\varphi - \varphi_n)
63+
64+
where $(s_n, \varphi_n) = (+1,\ 0),\ (-1,\ \pi/2),\ (+1,\ \pi),\ (-1,\ 3\pi/2)$
65+
for $n = 1, 2, 3, 4$ respectively.
66+
67+
Each arm has length $L$, core radius $R$, shell thickness $t$, and hence outer
68+
radius $R + t$.
69+
70+
.. note::
71+
72+
Each of the four arms is modelled as a complete cylinder of length $L$
73+
extending from the central junction, so the arms overlap near the origin.
74+
This overlap is neglected in two ways, following the treatment used in the
75+
reference. First, the particle volume is overestimated (the effect being
76+
largest when the arms are short and wide), which affects the calculated
77+
intensity through the volume normalisation. Second, the scattering amplitude
78+
is the plain sum of the four cylinder amplitudes, so the overlapping region
79+
near the origin is counted more than once; because this region is small
80+
compared with the arms, the resulting artefacts are expected to appear
81+
mainly in the high-$q$ range. Consequently the model is only valid for
82+
long-arm tetrapods, i.e. for arm lengths much larger than the arm width,
83+
$L \gg R + t$.
84+
85+
References
86+
----------
87+
88+
#. Seoki Kyoo Seo *Korean J. Chem. Eng.* 34(2017) 1192-1198 DOI:10.1007/s11814-016-0341-x
89+
90+
Authorship and Verification
91+
----------------------------
92+
93+
* **Author:** Yuhei Yamada (Github user name: Indigo Carmine, https://orcid.org/0009-0003-9780-4135)
94+
* **Last Modified by:**
95+
"""
96+
97+
from numpy import inf
98+
99+
name = "tetrapod"
100+
title = "Core-shell tetrapod with four cylindrical arms"
101+
description = """
102+
Calculates the scattering from a core-shell tetrapod structure with four
103+
cylindrical arms radiating from a central point. Each arm has a cylindrical
104+
core and a coaxial shell.
105+
"""
106+
category = "shape:cylinder"
107+
108+
# [ "name", "units", default, [lower, upper], "type", "description"],
109+
parameters = [
110+
["length", "Ang", 1000, [0, inf], "volume", "Arm length"],
111+
["radius", "Ang", 50, [0, inf], "volume", "Arm core radius"],
112+
["thickness", "Ang", 10, [0, inf], "volume", "Arm shell thickness"],
113+
["sld_core", "1e-6/Ang^2", 1, [-inf, inf], "sld", "Arm core scattering length density"],
114+
["sld_shell", "1e-6/Ang^2", 0.5, [-inf, inf], "sld", "Arm shell scattering length density"],
115+
["sld_solvent", "1e-6/Ang^2", 0, [-inf, inf], "sld", "Solvent scattering length density"],
116+
]
117+
118+
source = ["lib/polevl.c", "lib/sas_J1.c", "lib/gauss76.c", "tetrapod.c"]
119+
have_Fq = False
120+
opencl = True
121+
122+
123+
radius_effective_modes = [
124+
"equivalent volume sphere",
125+
"length of tetrapod arms (L)",
126+
]
127+
128+
129+
test = [
130+
# thickness=0 reduces to uniform cylinder: contrast = sld_core - sld_solvent = 1
131+
[{"thickness": 0}, 1.099643429303014e-03, 2.746377685546875e03],
132+
[{"thickness": 0}, 1.033727919136565e-01, 4.374285638332367e-01],
133+
[{"thickness": 0}, 3.145051218117226e-01, 3.527995198965073e-03],
134+
# default core-shell parameters
135+
[{}, 1.099643429303014e-03, 1.846798618097820e03],
136+
[{}, 1.033727919136565e-01, 1.811197691988146e-01],
137+
[{}, 3.145051218117226e-01, 1.080396557066592e-03],
138+
]

0 commit comments

Comments
 (0)