-
Notifications
You must be signed in to change notification settings - Fork 30
Adding torus elliptical shell model #704
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
base: master
Are you sure you want to change the base?
Changes from 3 commits
54f8635
9cf010f
1c758a0
c5e33d4
0392af7
376dd24
814325d
2cb3bdd
3b2dd1c
798a466
e93a806
e03af83
3149883
490b663
c0eac41
9d22ce9
c9072a9
2968487
bc8c875
85e1955
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| static double form_volume(double radius, double core_radius, double thickness, | ||
| double nu) { | ||
| double a = core_radius; | ||
| double b = nu * a; | ||
| double ao = a + thickness; | ||
| double bo = nu * ao; | ||
| double area = M_PI * ao * bo - M_PI * a * b; | ||
| return 2.0 * M_PI * radius * area; | ||
| } | ||
|
|
||
| static double F_torus(double Q, double theta, double R, double x, double nu, | ||
| double delta_eta) { | ||
| // integral_{R - x}^{R + x} | ||
| // 4 * pi * r * J0(Q * r * sin(theta)) * ganmma | ||
| // sinx_x(Q * ganmma * cos(theta)) dr | ||
|
|
||
| // Set lower integration bound to R-x (not min(R-x,0)), since a torus has a | ||
| // hole and R-x > 0 is always valid. | ||
|
|
||
| double gamma, int_r_delta, r, f_total, gamma_arg; | ||
| const double square_x = square(x); | ||
| const double Q_sin_theta = Q * sin(theta); | ||
| const double Q_cos_theta = Q * cos(theta); | ||
|
|
||
| f_total = 0.0; | ||
|
|
||
| if (Q_sin_theta == 0.0) { | ||
| // If Q * sin(theta) is zero, the integral simplifies to a constant | ||
| // value since J0(0) = 1 and sin(0) = 0. | ||
| return 4.0 * M_PI * R * x * | ||
| delta_eta; // integral over [R - x, R + x] is just a constant | ||
| } | ||
|
|
||
| for (int i = 0; i < GAUSS_N; i++) { | ||
| // translate a point in[-1, 1] to a point in[R - x, R + x] | ||
| r = GAUSS_Z[i] * x + R; | ||
|
|
||
| // gamma = nu * sqrt(fmax(square_x - square(r - R), 0.0)); | ||
| #ifdef USE_OPENCL // if you can use OpenCL, use the step function. | ||
| gamma_arg = square_x - square(r - R); | ||
| gamma = nu * sqrt(fabs(gamma_arg)) * step(0.0, gamma_arg); | ||
| #else | ||
| gamma = nu * sqrt(fmax(square_x - square(r - R), 0.0)); | ||
| #endif | ||
|
pkienzle marked this conversation as resolved.
Outdated
|
||
|
|
||
| int_r_delta = | ||
| r * sas_J0(r * Q_sin_theta) * sin(gamma * Q_cos_theta) / Q_cos_theta; | ||
|
pkienzle marked this conversation as resolved.
Outdated
|
||
| f_total += GAUSS_W[i] * int_r_delta; | ||
| } | ||
|
|
||
| return 4.0 * M_PI * f_total * x * | ||
| delta_eta; // multiply by x to get the integral over [R - x, R + x] | ||
| } | ||
|
|
||
| static double Iq_non_correlation(double q, double radius, double core_radius, | ||
| double thickness, double nu, double sld_core, | ||
| double sld_shell, double sld_solvent) { | ||
| // integral_{0}^{pi / 2} | ||
| // | F_torus(Q, theta, R, core_radius + thickness, nu, sld_shell - | ||
| // sld_solvent) // shell | ||
| // - F_torus(Q, theta, R, core_radius, nu, sld_core - sld_solvent) // | ||
| // core | ||
| // |^2 dtheta | ||
| double F_diff, theta; | ||
| double I_total = 0.0; | ||
|
|
||
| for (int i = 0; i < GAUSS_N; i++) { | ||
| // translate a point in[-1, 1] to a point in[0, pi / 2] | ||
| theta = GAUSS_Z[i] * M_PI_4 + M_PI_4; | ||
|
|
||
| F_diff = F_torus(q, theta, radius, core_radius + thickness, nu, | ||
| sld_shell - sld_solvent) - | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you define a separate aspect ratio for thickness,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your suggestion. I agree that this approach would be well suited to micelle-like systems. However, in our system (see the figure below from Ref. 3 by Martin et al.), the aspect ratio appears to be the same throughout. Therefore, we would prefer to keep a single parameter, ν, for fitting. Would you have any suggestions for how we might improve the model under this assumption?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whether we have a model with independent or shared aspect ratios depends on the community. If they are independent you can constrain them to be shared for your use case, but it is more work for you. If they are shared, then anyone wanting them independent will have to implement their own version of the model. Without dispersion in nu, you can tie M1.nu_t = M1.nu on the constrained fitting page in sasview. With dispersion this will integrate over the outer product nu × nu_t, which is not what you want. Instead you need to reparameterize the model with nu_t = nu (docs here) so that you only have dispersion in shared_nu. The following should work: from numpy import inf
from sasmodels.core import reparameterize
parameters = [
# name, units, default, [min, max], type, description
["shared_nu", "", 1, [0, inf], "volume", "aspect ratio"],
]
translation = """
nu = shared_nu
nu_t = shared_nu
"""
model_info = reparameterize('torus_elliptical_shell', parameters, translation, __file__)You could perhaps simplify it to the following so that you only have the nu parameter, but I haven't tested: ...
parameters = []
translation = "nu_t = nu"
...A question for the broader sasmodels/sasview community: do we want many variations on the same model in the sasmodels library, or do we want one base version with different variants selected via constraints (#215). For example, you can parameterize an ellipsoid by some combination of volume, aspect ratio, polar radius and equatorial radius. For a core-shell ellipsoid, you could further choose a hollow variant (with core sld implicitly equal to solvent sld, and volume fraction calculated from only the material in the shell) and a filled variant (where volume fraction includes the enclosed volume even when core sld is constrained to equal solvent sld). I think the best answer is to build variants into the model infrastructure so that the underlying kernel code is shared across all variants. This will require some additional work in the sasview model selector so that the different parameterizations can be selected. Meanwhile, I'll leave it to others to comment on which variant(s) of toroidal models should be supported in sasmodels.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand your recommendation. Adding many variations to sasmodels might make the implementation less clear. I’ve implemented the changes as suggested for now. If using variants in SasView is preferred, I’d be happy to help with that—please feel free to @mention me. |
||
| F_torus(q, theta, radius, core_radius, nu, sld_shell - sld_core); | ||
|
|
||
| I_total += GAUSS_W[i] * square(F_diff) * sin(theta); | ||
| } | ||
|
|
||
| return I_total * | ||
| M_PI_4; // multiply by pi/4 to get the integral over [0, pi/2] | ||
| } | ||
|
|
||
| static double Iq(double q, double radius, double core_radius, double thickness, | ||
| double nu, double sld_core, double sld_shell, | ||
| double sld_solvent, double zeta, double kappa) { | ||
| double Sq = 1 + (kappa / (1 + square(q * zeta))); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not seeing the structure factor term in the cited references. Can you break the structure factor out into a separate model? Or is it specific to the torus model? If you break out the S(q) term then you can define an F(q) functions which returns and <F²> needed for the β approximation.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for your comment. This is not specific to the torus model, but since we often use a structure factor together with it, I included it here for convenience. However, it may not be the best fit in this context, so I will provide the structure factor separately in another issue or pull request. From the Supporting Information of Ref. 3 (M. J. Hollamby et al.):
|
||
|
|
||
| return Sq * Iq_non_correlation(q, radius, core_radius, thickness, nu, | ||
| sld_core, sld_shell, sld_solvent); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| r""" | ||
| Definition | ||
| ---------- | ||
|
|
||
| This model computes the scattering from a torus with an elliptical tube | ||
| cross-section and a concentric shell. | ||
|
|
||
| The geometric parameters are: | ||
|
|
||
| * $R$: major (ring) radius of the torus centerline | ||
| * $a$: core minor radius in the radial direction | ||
| * $\nu$: aspect ratio of the elliptical cross-section | ||
| * $t$: shell thickness | ||
|
|
||
| .. figure:: img/torus_elliptical_shell_geometry.png | ||
|
|
||
| Schematic geometry of the torus with elliptical tube cross-section. | ||
|
|
||
| The same aspect ratio $\nu$ is used for both the core and outer cross-sections, | ||
| so the outer semi-axes are $a+t$ and $\nu(a+t)$. | ||
|
|
||
| For a given orientation angle $\theta$ between the torus symmetry axis and | ||
| $\vec q$, the kernel evaluates the amplitude using a numerical integral over | ||
| the tube section: | ||
|
|
||
| .. math:: | ||
|
|
||
| F(q,\theta; x, \Delta\rho) | ||
| = 4\pi\,\Delta\rho\int_{R-x}^{R+x} | ||
| r\,J_0\!\left(qr\sin\theta\right) | ||
| \frac{\sin\!\left(q\,\gamma\cos\theta\right)}{q\cos\theta}\,dr | ||
|
|
||
| with | ||
|
|
||
| .. math:: | ||
|
|
||
| \gamma = \nu\sqrt{x^2-(r-R)^2} | ||
|
|
||
| The core-shell amplitude is formed from outer and inner contributions: | ||
|
|
||
| .. math:: | ||
|
|
||
| F_{cs}(q,\theta) = | ||
| F\!\left(q,\theta;a + t,\rho_{shell}-\rho_{solvent}\right) | ||
| -F\!\left(q,\theta;a,\rho_{shell}-\rho_{core}\right) | ||
|
|
||
| and the orientationally averaged intensity is | ||
|
|
||
| .. math:: | ||
|
|
||
| I(q) = S(q)\int_0^{\pi/2}\!\left|F_{cs}(q,\theta)\right|^2\sin\theta\,d\theta | ||
|
|
||
| where | ||
|
|
||
| .. math:: | ||
|
|
||
| S(q)=1+\frac{\kappa}{1+(q\zeta)^2} | ||
|
|
||
| Here $S(q)$ represents the structure factor accounting for | ||
| interparticle correlations. | ||
|
|
||
| .. note:: | ||
|
|
||
| The C kernel integrates over $r \in [R-x, R+x]$, so the model assumes an | ||
| open torus hole and is only geometrically meaningful when $R > a+t$. | ||
| (Probably, you don't call it a donut if the hole is closed!) | ||
|
|
||
|
pkienzle marked this conversation as resolved.
|
||
| References | ||
| ---------- | ||
| #. T. Kawaguchi, *J. Appl. Crystallogr*, 34(2001) 580-584 | ||
| #. S. Förster, *J. Phys. Chem.*, 103(1999) 6657-6668 | ||
| #. M. J. Hollamby, *Angew. Chem. Int. Ed.* 55(2016) 9890 | ||
|
|
||
| Authorship and Verification | ||
| --------------------------- | ||
|
|
||
| * **Author:** Itsuki Tajima and Yuhei Yamada (Github username: Indigo Carmine, https://orcid.org/0009-0003-9780-4135) | ||
|
pkienzle marked this conversation as resolved.
Outdated
|
||
| * **Last Modified by:** | ||
| * **Last Reviewed by:** | ||
| """ | ||
|
|
||
| from numpy import inf | ||
|
|
||
| # ---- SasView model metadata ------------------------------------------------- | ||
| name = "torus_elliptical_shell" | ||
| title = "core-shell torus with elliptical cross-section" | ||
| description = "Core-shell torus with elliptical tube cross-section" | ||
| category = "shape:cylinder" | ||
| parameters = [ | ||
| # name units default [min, max] type description | ||
| ["radius", "Ang", 100.0, [0, inf], "volume", "Torus major radius R"], | ||
| [ | ||
| "core_radius", | ||
|
pkienzle marked this conversation as resolved.
Outdated
|
||
| "Ang", | ||
| 5.0, | ||
| [0, inf], | ||
| "volume", | ||
| "Elliptical core minor radius a", | ||
| ], | ||
| ["thickness", "Ang", 2.0, [0, inf], "volume", "Shell thickness"], | ||
| ["nu", "", 1.0, [0.1, 10.0], "volume", "Aspect ratio b/a"], | ||
| ["sld_core", "1e-6/Ang^2", 0.0, [-inf, inf], "sld", "Core SLD"], | ||
| ["sld_shell", "1e-6/Ang^2", 1.0, [-inf, inf], "sld", "Shell SLD"], | ||
| ["sld_solvent", "1e-6/Ang^2", 0.0, [-inf, inf], "sld", "Solvent SLD"], | ||
| ["zeta", "Ang", 0.0, [-inf, inf], "", "a correlation length"], | ||
| [ | ||
| "kappa", | ||
| "", | ||
| 0.0, | ||
| [-inf, inf], | ||
| "", | ||
| "the strength of interactions via the isothermal compressibility", | ||
| ], | ||
| ] | ||
|
|
||
| # -- tell sasmodels that a C kernel is provided ------------------------------- | ||
| source = [ | ||
| "lib/polevl.c", | ||
| "lib/sas_j0.c", | ||
| "lib/gauss76.c", | ||
| "torus_elliptical_shell.c", | ||
| ] # compiled together with default libs | ||
|
pkienzle marked this conversation as resolved.
|
||

Uh oh!
There was an error while loading. Please reload this page.