diff --git a/RATapi/controls.py b/RATapi/controls.py index a6e239eb..fe77e1ff 100644 --- a/RATapi/controls.py +++ b/RATapi/controls.py @@ -36,43 +36,99 @@ } -class Controls(BaseModel, validate_assignment=True, extra="forbid"): +class Controls(BaseModel, validate_assignment=True, extra="forbid", use_attribute_docstrings=True): """The full set of controls parameters for all five procedures that are required for the compiled RAT code.""" # All Procedures procedure: Procedures = Procedures.Calculate + """Which procedure RAT should execute. Can be 'calculate', 'simplex', 'de', 'ns', or 'dream'.""" + parallel: Parallel = Parallel.Single + """How the calculation should be parallelised. Can be 'single', 'contrasts' or 'points'.""" + calcSldDuringFit: bool = False + """Whether SLD will be calculated during fit (for live plotting etc.)""" + resampleMinAngle: float = Field(0.9, le=1, gt=0) + """The upper threshold on the angle between three sampled points for resampling, in units of radians over pi.""" + resampleNPoints: int = Field(50, gt=0) + """The number of initial points to use for resampling.""" + display: Display = Display.Iter + """How much RAT should print to the terminal. Can be 'off', 'iter', 'notify', or 'final'.""" + # Simplex xTolerance: float = Field(1.0e-6, gt=0.0) + """[SIMPLEX] The termination tolerance for step size.""" + funcTolerance: float = Field(1.0e-6, gt=0.0) + """[SIMPLEX] The termination tolerance for change in chi-squared.""" + maxFuncEvals: int = Field(10000, gt=0) + """[SIMPLEX] The maximum number of function evaluations before the algorithm terminates.""" + maxIterations: int = Field(1000, gt=0) + """[SIMPLEX] The maximum number of iterations before the algorithm terminates.""" + # Simplex and DE updateFreq: int = 1 + """[SIMPLEX, DE] Number of iterations between printing progress updates to the terminal.""" + updatePlotFreq: int = 20 + """[SIMPLEX, DE] Number of iterations between updates to live plots.""" + # DE populationSize: int = Field(20, ge=1) + """[DE] The number of candidate solutions that exist at any time.""" + fWeight: float = Field(0.5, gt=0.0) + """[DE] The step size for how different mutations are to their parents.""" + crossoverProbability: float = Field(0.8, gt=0.0, lt=1.0) + """[DE] The probability of exchange of parameters between individuals at any iteration.""" + strategy: Strategies = Strategies.RandomWithPerVectorDither + """[DE] The algorithm used to generate new candidates.""" + targetValue: float = Field(1.0, ge=1.0) + """[DE] The value of chi-squared at which the algorithm will terminate.""" + numGenerations: int = Field(500, ge=1) + """[DE] The maximum number of iterations before the algorithm terminates.""" + # NS nLive: int = Field(150, ge=1) + """[NS] The number of points to sample.""" + nMCMC: int = Field(0, ge=0) + """[NS] If non-zero, an MCMC process with ``nMCMC`` chains will be used instead of MultiNest.""" + propScale: float = Field(0.1, gt=0.0, lt=1.0) + """[NS] A scaling factor for the ellipsoid generated by MultiNest.""" + nsTolerance: float = Field(0.1, ge=0.0) + """[NS] The tolerance threshold for when the algorithm should terminate.""" + # Dream nSamples: int = Field(20000, ge=0) + """[DREAM] The number of samples in the initial population for each chain.""" + nChains: int = Field(10, gt=0) + """[DREAM] The number of Markov chains to use in the algorithm.""" + jumpProbability: float = Field(0.5, gt=0.0, lt=1.0) + """[DREAM] The probability range for the size of jumps in sampling. Larger values mean more variable jumps.""" + pUnitGamma: float = Field(0.2, gt=0.0, lt=1.0) + """[DREAM] The probability that the scaling-down factor of jumps will be ignored and a larger jump will be taken.""" + boundHandling: BoundHandling = BoundHandling.Reflect + """[DREAM] How steps past the space boundaries should be handled. Can be 'off', 'reflect', 'bound', or 'fold'.""" + adaptPCR: bool = True + """[DREAM] Whether the crossover probability for differential evolution should be adapted during the run.""" + # Private field for IPC file _IPCFilePath: str = "" diff --git a/RATapi/examples/non_polarised/DSPC_custom_XY.py b/RATapi/examples/non_polarised/DSPC_custom_XY.py index 647f302b..ab1492da 100644 --- a/RATapi/examples/non_polarised/DSPC_custom_XY.py +++ b/RATapi/examples/non_polarised/DSPC_custom_XY.py @@ -6,7 +6,7 @@ def DSPC_custom_XY(): - """Custom XY Example for Supported DSPC layer. + r"""Custom XY Example for Supported DSPC layer. In this example, we model the same data (DSPC supported bilayer) as the Custom Layers example, but this time we will use continuous distributions of the volume fractions of each component to build up the SLD profiles (as described in @@ -19,7 +19,7 @@ def DSPC_custom_XY(): We can define our lipid in terms of an Area per Molecule, almost in its entirety, if we recognise that where the volume is known, the thickness of the layer is simply given by the layer volume / APM: - $d_{\textrm{layer}} =\frac{V_{\textrm{layer}} }{{\textrm{APM}}_{\textrm{layer}}}$. + .. math:: d_{\textrm{layer}} =\frac{V_{\textrm{layer}} }{{\textrm{APM}}_{\textrm{layer}}}. We can then define the Volume Fraction of this layer with a roughened Heaviside of length dlayer and a height of 1. Then, the total volume occupied will be given by the sum of the volume fractions across the interface. Of course, @@ -27,7 +27,7 @@ def DSPC_custom_XY(): functions by relevant coverage parameters. When this is correctly done, we can obtain the remaining water distribution as: - $${\textrm{VF}}_{\textrm{wat}} =1-\\sum_n {\textrm{VF}}_n$$ + .. math:: {\textrm{VF}}_{\textrm{wat}} =1-\\sum_n {\textrm{VF}}_n where VFn is the Volume Fraction of the n'th layer. """ diff --git a/RATapi/models.py b/RATapi/models.py index 095badbe..3d9f7097 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -94,11 +94,14 @@ class Background(Signal): The type of background (constant, function or data) source : str The source of the background; + - if type is 'constant', this should be the name of a background parameter. - if type is 'data', this should be the name of a dataset defined in `Project.data`. - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. - value_1, value_2, ..., value_5 : str + + value_1, value_2, value_3, value_4, value_5 : str Values required by the background. + - if type is 'constant', all values will be ignored. - if type is 'data', value_1 may be the parameter name for an optional offset. Other values are ignored. - if type is 'function', these values may be the names of up to 5 parameters which are passed to the function. @@ -318,6 +321,8 @@ def set_matlab_function_name(self): class Data(RATModel, arbitrary_types_allowed=True): """A dataset required for a contrast. + Parameters + ---------- name : str The name of this dataset. data : np.ndarray[np.float64] @@ -584,12 +589,15 @@ class Resolution(Signal): The type of resolution: 'constant', 'data', or (NOT YET IMPLEMENTED) 'function'. source : str The source data for the resolution; + - if type is 'constant', this should be the name of a background parameter. - if type is 'data', this should be empty (resolution data is in the contrast data). - if type is 'function' (NOT YET IMPLEMENTED), this should be the name of a custom function defined in `Project.custom_files`. - value_1, value_2, ..., value_5 : str + + value_1, value_2, value_3, value_4, value_5 : str Values required by the background. + - if type is 'constant' or 'data', all values will be ignored. - if type is 'function' (NOT YET IMPLEMENTED), these values may be the names of up to 5 parameters which are passed to the function. diff --git a/RATapi/project.py b/RATapi/project.py index b8c51e5e..71de9476 100644 --- a/RATapi/project.py +++ b/RATapi/project.py @@ -123,7 +123,7 @@ def discriminate_contrasts(contrast_input): ] -class Project(BaseModel, validate_assignment=True, extra="forbid"): +class Project(BaseModel, validate_assignment=True, extra="forbid", use_attribute_docstrings=True): """Defines the input data for a reflectivity calculation in RAT. This class combines the data defined in each of the pydantic models included in "models.py" into the full set of @@ -131,12 +131,22 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): """ name: str = "" + """The name of the project.""" + calculation: Calculations = Calculations.Normal + """What calculation type should be used. Can be 'normal' or 'domains'.""" + model: LayerModels = LayerModels.StandardLayers + """What layer model should be used. Can be 'standard layers', 'custom layers', or 'custom xy'.""" + geometry: Geometries = Geometries.AirSubstrate + """What geometry should be used. Can be 'air/substrate' or 'substrate/liquid'""" + absorption: bool = False + """Whether imaginary SLD (absorption) should be accounted for.""" parameters: ClassList[RATapi.models.Parameter] = ClassList() + """The list of parameters used in the layers of a model.""" bulk_in: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -150,6 +160,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for SLD of the entry interfaces of a model.""" bulk_out: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -163,6 +174,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for SLD of the exit interfaces of a model.""" scalefactors: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -176,6 +188,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for scale factors to handle systematic error in model data.""" domain_ratios: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -189,6 +202,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for weighting between domains of a domains model.""" background_parameters: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -202,10 +216,12 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for models of backgrounds.""" backgrounds: ClassList[RATapi.models.Background] = ClassList( RATapi.models.Background(name="Background 1", type=TypeOptions.Constant, source="Background Param 1"), ) + """The list of models for background noise in the project.""" resolution_parameters: ClassList[RATapi.models.Parameter] = ClassList( RATapi.models.Parameter( @@ -219,13 +235,19 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): sigma=np.inf, ), ) + """The list of parameters for models of resolutions.""" resolutions: ClassList[RATapi.models.Resolution] = ClassList( RATapi.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, source="Resolution Param 1"), ) + """The list of models for instrument resolution in the project.""" custom_files: ClassList[RATapi.models.CustomFile] = ClassList() + """Handles for custom files used by the project.""" + data: ClassList[RATapi.models.Data] = ClassList() + """Experimental data for a model.""" + layers: Union[ Annotated[ClassList[RATapi.models.Layer], Tag("no_abs")], Annotated[ClassList[RATapi.models.AbsorptionLayer], Tag("abs")], @@ -238,7 +260,11 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): custom_error_context={"discriminator": "absorption_or_no"}, ), ) + """The layers of a standard layer model.""" + domain_contrasts: ClassList[RATapi.models.DomainContrast] = ClassList() + """The groups of layers required by each domain in a domains model.""" + contrasts: Union[ Annotated[ClassList[RATapi.models.Contrast], Tag("no_ratio")], Annotated[ClassList[RATapi.models.ContrastWithRatio], Tag("ratio")], @@ -251,6 +277,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): custom_error_context={"discriminator": "ratio_or_no_ratio"}, ), ) + """All groups of components used to define each model in the project.""" _all_names: dict _contrast_model_field: str