Skip to content

Conversation

@aglavic
Copy link
Collaborator

@aglavic aglavic commented Jun 6, 2025

This is the first step towards expanding the ORSO model language to more complex cases including physics models in soft-matter. Please be free to give your opinions and ideas for improvements.

Features that were added:

  • Resolve names from SLDdb directly
  • Resolve ID={int} from SLDdb directly
  • Option to resolve complex items using custom resolvers (not tested)
  • Possibility to describe a set of layers/a sample element with a separate class. These are then references in the sub_stacks dictionary. Two examples were implemented:
    • LipidLeaflet - similar to refnx
    • FunctionTwoElements
  • For such elements that are meant to be embedded, a new syntax is allowed in the sample string the places the items into a material.
  • New global parameters for the slicing resolution of analytical SLD models and default sample solvent.
  • Possibility to add an object so sub_stacks that is compying another one with changes to user-defined parameters.

Example of a set of lipi bi-layers:

sample:
    model:
        stack: Si  | ( LL | rLL ) in D2O | 2 ( LL | rLL ) |  water
        sub_stacks:
            LL:
                sub_stack_class: LipidLeaflet
                apm: 56.0
                b_heads: 6.01e-4
                vm_heads: 319.0
                b_tails: -2.92e-4
                vm_tails: 782.0
                thickness_heads: 9.0
                thickness: 23.0
            rLL:
                like: LL
                but:
                    reverse_monolayer: true
        globals:
            length_unit: angstrom
            roughness: {magnitude: 3.0, unit: angstrom}
            default_solvent: {formula: 'H2O', mass_density: {magnitude: 1.0, unit: g/cm^3}}

Example of a user defined functional form of SLD profile:

sample:
    model:
        comment: Saw tooth sample model
        stack: air | gtop 50 | 5 (rgradient 50 | gradient 50) | Si
        sub_stacks:
          gradient:
            sub_stack_class: FunctionTwoElements
            material1: Ni
            material2: Ti
            function: x
          rgradient:
            like: gradient
            but:
              function: 1-x
          gtop:
            like: gradient
            but:
              roughness: 15.0
        globals:
            slice_resolution: {magnitude: 10.0, unit: nm}

aglavic added 22 commits May 12, 2025 17:05
…efinition

For SLD db resolver search for name or interpret ID={number} directly
…x model object for analytic function for mixing materials
… automatically generated by black for "def a(b): ..."
…SubStack depth that might become indefinately deep
Copy link

@tomarnoldess tomarnoldess left a comment

Choose a reason for hiding this comment

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

This looks good enough for review at the coming orso meeting.
The rLL decription should allow for one or more of the variables defined in LL to be redefined as well as being put in the reverse order.
i'm not sure i understand the top level syntax: Si | ( LL | rLL ) in D2O | 2 ( LL | rLL ) | water

  • what is the "2" doing in this?
  • what is the point of defining D2O as part of one of the bilayers? is it different from the water? how does it relate to the "default_solvent"?
  • are there some implied constraints?

@aglavic
Copy link
Collaborator Author

aglavic commented Jun 18, 2025

@tomarnoldess

The stack string syntax is basically an extension of what was already there for SubStack grouping.

Everythig inside of brackets is a stack within a (unnamed) SubStack. A number before defines number of repetitions.

The like:... but:... syntax allows any such objects to be replaced with arbitrary parameters being overwritten. In that sense it implies some kind of constraints for everything that is not changed. Other implided constraints come from using the same materials at different placed.
But generally, constraints in the sense of model fitting are not part of the model description.

Currently the definition of the LipidLeaflet is based on me lookin in the refnx docs, just adopting the total thickness to be a separate parameter so one can use the stack string to set it. In refnx I saw that all of this is defined within some kind of solvent and that this could actually come from some sort of parent structure.
From that I had the idea to allow to simplify the sample description for users by defining a generic solvent for the sample (the global default_solvent) that would be used if the model does not define solvents specifically.
A second option is to define a solvent for everything within a SubStack (environment parameter). In the stack string syntax this is done by using the "in" keyword after the stack.

So Si | ( LL | rLL ) in D2O | 2 ( LL | rLL ) | water means:

  • Beam comes from Silicon
  • hits LL/rLL bilaryer with D2O as solvent
  • hits 2x LL/rLL bilayer with default_solvent
  • backing medium is water (special material keyword that is pre-defined)

As I don't have any idea how these models are typically build, some of these assumptions may be wrong. So if you don't find the solvent option actually useful, we can think how to parameterize such structures best.

@andyfaff
Copy link
Contributor

Sorry, I've not had chance to read the PR yet. However, some explanation of refnx design may be useful here.

A refnx Structure is composed of several Component chained together. The simplest Component is a Slab, another is LipidLeaflet. Each Component is responsible for rendering a slab representation of itself, consisting of multiple rows of thickness / SLD.real / SLD.imag / roughness /solvent fraction. A Slab would have one row, a LipidLeaflet can be described by two rows (heads/tails).
A Component normally delegates the effect of solvent ingress to the parent Structure. However, it can handle it itself by adjusting the the nuclear SLD and setting the solvent fraction to zero.
A Structure is normally responsible for handling solvent ingress. It sees how much solvent fraction is in each of the slabs reported by each Component and does the simple calculation of weighting the nuclear SLD of the slab with the solvating material.
By default the Structure assumes that the backing medium is doing the solvation, because that makes things simple for solid-liquid NR. However, that approach fails for liquid-solid XRR because it's the fronting medium doing the solvation (beam comes in through liquid). In that case one can set a Structure.solvent attribute to change the default behaviour.

Now let's consider a LipidLeaflet Component. This object can just use the default solvation provided by the parent Structure. That's works for the most part, until you have something like a monolayer at the air-liquid interface, where the tails are solvated by air, and the heads are solvated by the subphase. In that case head_solvent and tail_solvent parameters to LipidLeaflet individually dictate by what material the tail and head sublayer regions are solvated by.

Don't forget that LipidLeaflet is parameterised by physical parameters such as area-per-molecule, volume of tail group, sum of scattering lengths of tail group, etc. Moreover it has a reverse_monolayer attribute that flips the orientation of the molecules, so that by using successive leaflets one can have a bilayer.
By default the heads are oriented towards the fronting medium, so if you want to have a bilayer at the solid liquid interface the first monolayer has reverse_monolayer==False, the second monolayer has reverse_monolayer==True. For a monolayer at the air-liquid interface reverse_monolayer==True.

Most non-Slab Component have their own parameterisation, typically with physically relevant parameters (c.f. AreaPerMolecule/decay length/etc).

These design details add a lot of sophistication and power to the analysis. However, the sophistication may be difficult to insert into a common framework like the ORSO model language, as different analysis package approaches things slightly differently.

For an example of how LipidLeaflet are used, please see https://github.com/refnx/refnx/blob/main/paper/supporting_information/lipid.ipynb. That notebook is using the default approach, i.e. the Structure handling all solvation.

@aglavic
Copy link
Collaborator Author

aglavic commented Jun 27, 2025

As was discussed during the sample model session, we will merge these changes already agreed. LipidLeaflet was removed as parameterization was not yead defined with the community. I'll create another PR speicfically for that discussion.

@aglavic aglavic merged commit b5efef6 into reflectivity:main Jun 27, 2025
8 checks passed
@aglavic aglavic deleted the expand_model_language branch June 27, 2025 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants