diff --git a/src/sas/qtgui/Calculators/GenericScatteringCalculator.py b/src/sas/qtgui/Calculators/GenericScatteringCalculator.py
index e9f9f09e52..2e28078c8e 100644
--- a/src/sas/qtgui/Calculators/GenericScatteringCalculator.py
+++ b/src/sas/qtgui/Calculators/GenericScatteringCalculator.py
@@ -5,6 +5,7 @@
import logging
import time
import timeit
+import math
from scipy.spatial.transform import Rotation
@@ -21,9 +22,14 @@
import periodictable
import sas.qtgui.Utilities.GuiUtils as GuiUtils
+from sas.qtgui.Utilities.TabbedModelEditor import TabbedModelEditor
from sas.qtgui.Utilities.GenericReader import GenReader
from sasdata.dataloader.data_info import Detector, Source
+from sas.system.version import __version__
from sas.sascalc.calculator import sas_gen
+from sas.sascalc.fit import models
+from sas.sascalc.calculator.geni import radius_of_gyration, create_beta_plot, f_of_q
+import sas.sascalc.calculator.gsc_model as gsc_model
from sas.qtgui.Plotting.PlotterBase import PlotterBase
from sas.qtgui.Plotting.Plotter2D import Plotter2D
from sas.qtgui.Plotting.Plotter import Plotter
@@ -35,9 +41,6 @@
# Local UI
from .UI.GenericScatteringCalculator import Ui_GenericScatteringCalculator
-_Q1D_MIN = 0.001
-
-
class GenericScatteringCalculator(QtWidgets.QDialog, Ui_GenericScatteringCalculator):
trigger_plot_3d = QtCore.Signal()
@@ -84,6 +87,7 @@ def __init__(self, parent=None):
self.is_beta = False
self.data_to_plot = None
self.data_betaQ = None
+ self.fQ = []
self.graph_num = 1 # index for name of graph
# finish UI setup - install qml window
@@ -100,7 +104,7 @@ def __init__(self, parent=None):
# code to highlight incompleted values in the GUI and prevent calculation
# list of lineEdits to be checked
self.lineEdits = [self.txtUpFracIn, self.txtUpFracOut, self.txtUpTheta, self.txtUpPhi, self.txtBackground,
- self.txtScale, self.txtSolventSLD, self.txtTotalVolume, self.txtNoQBins, self.txtQxMax,
+ self.txtScale, self.txtSolventSLD, self.txtTotalVolume, self.txtNoQBins, self.txtQxMax, self.txtQxMin,
self.txtMx, self.txtMy, self.txtMz, self.txtNucl, self.txtXnodes, self.txtYnodes,
self.txtZnodes, self.txtXstepsize, self.txtYstepsize, self.txtZstepsize, self.txtEnvYaw,
self.txtEnvPitch, self.txtEnvRoll, self.txtSampleYaw, self.txtSamplePitch, self.txtSampleRoll]
@@ -122,6 +126,8 @@ def __init__(self, parent=None):
# checkboxes
self.checkboxNucData.stateChanged.connect(self.change_data_type)
self.checkboxMagData.stateChanged.connect(self.change_data_type)
+ self.checkboxPluginModel.stateChanged.connect(self.update_file_name)
+ self.checkboxLogSpace.stateChanged.connect(self.change_qValidator)
self.cmdDraw.clicked.connect(lambda: self.plot3d(has_arrow=True))
self.cmdDrawpoints.clicked.connect(lambda: self.plot3d(has_arrow=False))
@@ -139,6 +145,10 @@ def __init__(self, parent=None):
self.txtMy.textChanged.connect(self.check_for_magnetic_controls)
self.txtMz.textChanged.connect(self.check_for_magnetic_controls)
+ #check for SLD changes
+ #TODO: Implement a scientifically sound method for obtaining protein volume - Current value is a inprecise approximation. Until then Solvent SLD does not impact RG - SLD.
+ # self.txtSolventSLD.editingFinished.connect(self.update_Rg)
+
#update coord display
self.txtEnvYaw.textChanged.connect(self.update_coords)
self.txtEnvPitch.textChanged.connect(self.update_coords)
@@ -215,15 +225,16 @@ def __init__(self, parent=None):
self.txtSampleRoll.setValidator(
QtGui.QRegularExpressionValidator(validat_regex_float, self.txtSampleRoll))
- # 0 < Qmax <= 1000
- validat_regex_q = QtCore.QRegularExpression(r'^1000$|^[+]?(\d{1,3}([.]\d+)?)$')
- self.txtQxMax.setValidator(QtGui.QRegularExpressionValidator(validat_regex_q,
- self.txtQxMax))
-
+ self.change_qValidator()
+
# 2 <= Qbin and nodes integers < 1000
validat_regex_int = QtCore.QRegularExpression(r'^[2-9]|[1-9]\d{1,2}$')
self.txtNoQBins.setValidator(QtGui.QRegularExpressionValidator(validat_regex_int,
self.txtNoQBins))
+
+ # Plugin File Name
+ rx = QtCore.QRegularExpression("^[A-Za-z0-9_]*$")
+ self.txtFileName.setValidator(QtGui.QRegularExpressionValidator(rx))
self.txtXnodes.setValidator(
QtGui.QRegularExpressionValidator(validat_regex_int, self.txtXnodes))
@@ -447,10 +458,23 @@ def gui_text_changed(self, sender):
zstepsize = float(self.txtZstepsize.text())
value = float(str(self.txtQxMax.text()))
max_q = numpy.pi / (max(xstepsize, ystepsize, zstepsize))
- if value <= 0 or value > max_q:
+ if value <= 0 or value > max_q or value < float(self.txtQxMin.text()):
self.txtQxMax.setStyleSheet(self.TEXTBOX_WARNING_STYLESTRING)
else:
self.txtQxMax.setStyleSheet(self.TEXTBOX_DEFAULT_STYLESTRING)
+ #if 2D scattering, program sets qmin to -qmax
+ if not self.is_avg:
+ self.txtQxMin.setText(str(-value))
+ elif sender == self.txtQxMin and self.is_avg:
+ xstepsize = float(self.txtXstepsize.text())
+ ystepsize = float(self.txtYstepsize.text())
+ zstepsize = float(self.txtZstepsize.text())
+ value = float(str(self.txtQxMin.text()))
+ min_q = numpy.pi / (min(xstepsize, ystepsize, zstepsize))
+ if value <= 0 or value > min_q or value > float(self.txtQxMax.text()):
+ self.txtQxMin.setStyleSheet(self.TEXTBOX_WARNING_STYLESTRING)
+ else:
+ self.txtQxMin.setStyleSheet(self.TEXTBOX_DEFAULT_STYLESTRING)
@@ -533,7 +557,21 @@ def change_data_type(self):
self.update_gui()
self.check_for_magnetic_controls()
+ def change_qValidator(self):
+ #Default Q range to calculate P(Q) is from 0.0003 to 0.3 A^-1.
+ #The Q range needed for calculating P(Q) depends on the protein size.
+ #It is perhaps better to use values based on RG in future.
+ #For example: Q_min * RG = 0.2 and Q_max * RG = 15 could be a good start
+ #Note: Fitting window defaults to .5 and .0005.
+ if float(self.txtQxMax.text()) == 0:
+ self.txtQxMax.setText("0.3")
+ if float(self.txtQxMin.text()) == 0:
+ self.txtQxMin.setText("0.0003")
+ min = 0 if not self.checkboxLogSpace.isChecked() else 1e-10
+ # 0 < Qmin&QMax <= 1000
+ self.txtQxMax.setValidator(QtGui.QDoubleValidator(min, 1000, 10, self.txtQxMax))
+ self.txtQxMin.setValidator(QtGui.QDoubleValidator(min, 1000, 10, self.txtQxMin))
def update_cbOptionsCalc_visibility(self):
# Only allow 1D averaging if no magnetic data and not elements
@@ -550,6 +588,9 @@ def update_cbOptionsCalc_visibility(self):
self.txtMx.setEnabled(not self.is_mag)
self.txtMy.setEnabled(not self.is_mag)
self.txtMz.setEnabled(not self.is_mag)
+ self.txtQxMin.setEnabled(not self.is_mag)
+ self.checkboxLogSpace.setChecked(not self.is_mag)
+ self.checkboxLogSpace.setEnabled(not self.is_mag)
def change_is_avg(self):
@@ -569,18 +610,25 @@ def change_is_avg(self):
# did user request Beta(Q) calculation?
self.is_beta = (self.cbOptionsCalc.currentIndex() == 2)
# If averaging then set to 0 and diable the magnetic SLD textboxes
- if self.is_avg:
- self.txtMx.setEnabled(False)
- self.txtMy.setEnabled(False)
- self.txtMz.setEnabled(False)
+ self.txtMx.setEnabled(not self.is_avg)
+ self.txtMy.setEnabled(not self.is_avg)
+ self.txtMz.setEnabled(not self.is_avg)
+ self.txtQxMin.setEnabled(self.is_avg)
+ self.checkboxLogSpace.setChecked(self.is_avg)
+ self.checkboxLogSpace.setEnabled(self.is_avg)
+ self.checkboxPluginModel.setEnabled(self.is_avg)
+
+ if self.is_avg:
self.txtMx.setText("0.0")
self.txtMy.setText("0.0")
self.txtMz.setText("0.0")
+ self.txtQxMin.setText(str(float(self.txtQxMax.text())*.001))
+
# If not averaging then re-enable the magnetic sld textboxes
else:
- self.txtMx.setEnabled(True)
- self.txtMy.setEnabled(True)
- self.txtMz.setEnabled(True)
+ self.txtQxMin.setText(str(float(self.txtQxMax.text())*-1))
+ self.checkboxPluginModel.setChecked(False)
+ self.txtFileName.setEnabled(False)
def check_for_magnetic_controls(self):
if self.txtMx.hasAcceptableInput() and self.txtMy.hasAcceptableInput() and self.txtMz.hasAcceptableInput():
@@ -765,6 +813,7 @@ def complete_loading(self, data=None, load_nuc=True):
self.update_gui()
# reset verification now we have loaded new files
self.verify = False
+
self.verified = self.model.file_verification(self.nuc_sld_data, self.mag_sld_data)
self.toggle_error_functionality()
@@ -861,68 +910,30 @@ def update_gui(self):
self.txtYstepsize.setText(GuiUtils.formatValue(self.mag_sld_data.ystepsize))
self.txtZstepsize.setText(GuiUtils.formatValue(self.mag_sld_data.zstepsize))
# otherwise leave as set since editable by user
+ self.update_Rg()
- # update the value of the Radius of Gyration with values from the loaded data
+
+ def update_Rg(self):
+ #update RG boxes or run RG calculation
if self.is_nuc:
if self.nuc_sld_data.is_elements:
- self.txtROG.setText(str("N/A for Elements"))
+ self.txtRgMass.setText(str("N/A"))
+ self.txtRG.setText(str("N/A "))
+ logging.info("SasView does not support computation of Radius of Gyration for elements.")
else:
- self.txtROG.setText(self.radius_of_gyration() + " Å")
+ rgVals = radius_of_gyration(self.nuc_sld_data) #[String, String, Float], float used for plugin model
+ self.txtRgMass.setText(rgVals[0])
+ self.txtRG.setText(rgVals[1])
+ self.rGMass = rgVals[2] #used in plugin model
+
elif self.is_mag:
- self.txtROG.setText(str("N/A for magnetic data"))
+ self.txtRgMass.setText(str("N/A"))
+ self.txtRG.setText(str("N/A"))
+ logging.info("SasView does not support computation of Radius of Gyration for Magnetic Data.")
else:
- self.txtROG.setText(str("N/A for no data"))
-
-
- # If nodes or stepsize changed then this may effect what values are allowed
- self.gui_text_changed(sender=self.txtNoQBins)
- self.gui_text_changed(sender=self.txtQxMax)
-
- def radius_of_gyration(self):
- #Calculate Center of Mass(CoM) First
- CoM = self.centerOfMass()
-
- #Now Calculate RoG
- RoGNumerator = RoGDenominator = 0.0
-
- for i in range(len(self.nuc_sld_data.pos_x)):
- coordinates = [float(self.nuc_sld_data.pos_x[i]),float(self.nuc_sld_data.pos_y[i]),float(self.nuc_sld_data.pos_z[i])]
-
- #Coh b - Coherent Scattering Length(fm)
- cohB = periodictable.elements.symbol(self.nuc_sld_data.pix_symbol[i]).neutron.b_c
-
- #Calculate the Magnitude of the Coordinate vector for the atom and the center of mass
- MagnitudeOfCoM = numpy.sqrt(numpy.power(CoM[0]-coordinates[0],2) + numpy.power(CoM[1]-coordinates[1],2) + numpy.power(CoM[2]-coordinates[2],2))
-
- #Calculate Rate of Gyration (Squared) with the formular
- RoGNumerator += cohB * (numpy.power(MagnitudeOfCoM,2))
- RoGDenominator += cohB
-
- #Avoid division by zero - May occur through contrast matching
- RoG = str(round(numpy.sqrt(RoGNumerator/RoGDenominator),1)) if RoGDenominator != 0 else "NaN"
-
- return RoG
-
- def centerOfMass(self):
- """Calculate Center of Mass(CoM) of provided atom"""
- CoMnumerator= [0.0,0.0,0.0]
- CoMdenominator = [0.0,0.0,0.0]
-
- for i in range(len(self.nuc_sld_data.pos_x)):
- coordinates = [float(self.nuc_sld_data.pos_x[i]),float(self.nuc_sld_data.pos_y[i]),float(self.nuc_sld_data.pos_z[i])]
+ self.txtRgMass.setText(str("No Data"))
+ self.txtRG.setText(str("No Data"))
- #Coh b - Coherent Scattering Length(fm)
- cohB = periodictable.elements.symbol(self.nuc_sld_data.pix_symbol[i]).neutron.b_c
-
- for j in range(3): #sets CiN
- CoMnumerator[j] += (coordinates[j]*cohB)
- CoMdenominator[j] += cohB
-
- CoM = []
- for i in range(3):
- CoM.append(CoMnumerator[i]/CoMdenominator[i] if CoMdenominator != 0 else 0) #center of mass, test for division by zero
-
- return CoM
def update_geometry_effects(self):
@@ -952,6 +963,7 @@ def update_geometry_effects(self):
# If nodes or stepsize changed then this may effect what values are allowed
self.gui_text_changed(sender=self.txtNoQBins)
self.gui_text_changed(sender=self.txtQxMax)
+ self.gui_text_changed(sender=self.txtQxMin)
def write_new_values_from_gui(self):
"""Update parameters in model using modified inputs from GUI
@@ -1017,6 +1029,7 @@ def onReset(self):
self.txtTotalVolume.setText("216000.0")
self.txtNoQBins.setText("30")
self.txtQxMax.setText("0.3")
+ self.txtQxMin.setText("0.0003")
self.txtNoPixels.setText("1000")
self.txtMx.setText("0.0")
self.txtMy.setText("0.0")
@@ -1163,17 +1176,28 @@ def _create_default_1d_data(self):
residuals.dxw = None
"""
self.qmax_x = float(self.txtQxMax.text())
+ self.qmin_x = float(self.txtQxMin.text())
self.npts_x = int(self.txtNoQBins.text())
+ self.xValues = numpy.array(self.npts_x)
# Default values
xmax = self.qmax_x
- xmin = self.qmax_x * _Q1D_MIN
+ xmin = self.qmin_x
qstep = self.npts_x
- x = numpy.linspace(start=xmin, stop=xmax, num=qstep, endpoint=True)
+ if self.checkboxLogSpace.isChecked():
+ self.xValues = numpy.logspace(start=math.log(xmin,10),
+ stop=math.log(xmax,10),
+ num=qstep,
+ endpoint=True)
+ else:
+ self.xValues = numpy.linspace(start=xmin,
+ stop=xmax,
+ num=qstep,
+ endpoint=True)
# store x and y bin centers in q space
- y = numpy.ones(len(x))
- dy = numpy.zeros(len(x))
- dx = numpy.zeros(len(x))
- self.data = Data1D(x=x, y=y)
+ y = numpy.ones(len(self.xValues))
+ dy = numpy.zeros(len(self.xValues))
+ dx = numpy.zeros(len(self.xValues))
+ self.data = Data1D(x=self.xValues, y=y)
self.data.dx = dx
self.data.dy = dy
@@ -1433,8 +1457,19 @@ def complete(self, input, update=None):
# if Beta(Q) Calculation has been requested, run calculation
if self.is_beta:
- self.create_betaPlot()
+ self.data_betaQ = create_beta_plot(self.xValues, self.nuc_sld_data, self.data_to_plot)
+ if self.checkboxPluginModel.isChecked():
+ self.fQ = f_of_q(self.xValues, self.nuc_sld_data)
+ model_str, model_path = gsc_model.generate_plugin(self.txtFileName.text(), self.data_to_plot, self.xValues,
+ self.fQ, self.rGMass)
+ TabbedModelEditor.writeFile(model_path, model_str)
+ self.manager.communicate.customModelDirectoryChanged.emit()
+
+ # Notify the user
+ msg = "Custom model " + str(model_path.absolute()) + " successfully created."
+ logging.info(msg)
+
self.cmdCompute.setText('Compute')
self.cmdCompute.setToolTip("
Compute the scattering pattern and display 1D or 2D plot depending on the settings.
")
self.cmdCompute.clicked.disconnect()
@@ -1442,55 +1477,31 @@ def complete(self, input, update=None):
self.cmdCompute.setEnabled(True)
return
- def create_betaPlot(self):
- """Carry out the compuation of beta Q using provided & calculated data
- Returns a list of BetaQ values
-
- """
-
- #Center Of Mass Calculation
- CoM = self.centerOfMass()
- self.data_betaQ = []
-
- # Default values
- xmax = self.qmax_x
- xmin = self.qmax_x * _Q1D_MIN
- qstep = self.npts_x
-
- currentQValue = []
- formFactor = self.data_to_plot
-
- for a in range(self.npts_x):
- fQ = 0
- currentQValue.append(xmin + (xmax - xmin)/(self.npts_x-1)*a)
-
- for b in range(len(self.nuc_sld_data.pos_x)):
- #atoms
- atomName = str(self.nuc_sld_data.pix_symbol[b])
- #Coherent Scattering Length of Atom
- cohB = periodictable.elements.symbol(atomName).neutron.b_c
-
- x = float(self.nuc_sld_data.pos_x[b])
- y = float(self.nuc_sld_data.pos_y[b])
- z = float(self.nuc_sld_data.pos_z[b])
-
- r_x = x - CoM[0]
- r_y = y - CoM[1]
- r_z = z - CoM[2]
-
- magnitudeRelativeCoordinate = numpy.sqrt(r_x**2 + r_y**2 + r_z**2)
- fQ += (cohB * (numpy.sin(currentQValue[a] * magnitudeRelativeCoordinate) / (currentQValue[a] * magnitudeRelativeCoordinate)))
-
- #Beta Q Calculation
- self.data_betaQ.append((fQ**2)/(formFactor[a]))
-
- #Scale Beta Q to 0-1
- scalingFactor = self.data_betaQ[0]
- self.data_betaQ = [x/scalingFactor for x in self.data_betaQ]
-
- return
+ def update_file_name(self):
+ if self.checkboxPluginModel.isChecked():
+ self.txtFileName.setText(self.getFileName())
+ self.txtFileName.setEnabled(True)
+ else:
+ self.txtFileName.setText("")
+ self.txtFileName.setEnabled(False)
+
+ def getFileName(self):
+ plugin_location = models.find_plugins_dir()
+ i = 0
+ while True:
+ full_path = os.path.join(plugin_location, "custom_gsc" + str(i))
+ if os.path.splitext(full_path)[1] != ".py":
+ full_path += ".py"
+ if not os.path.exists(full_path):
+ break
+ else:
+ i += 1
+
+ return ("custom_gsc" + str(i))
+
+
def onSaveFile(self):
"""Save data as .sld file"""
path = os.path.dirname(str(self.datafile))
@@ -1568,6 +1579,8 @@ def plot_1_2d(self):
int(self.graph_num))
dataBetaQ.xaxis(r'\rm{Q_{x}}', r'\AA^{-1}')
dataBetaQ.yaxis(r'\rm{Beta(Q)}', 'cm^{-1}')
+
+ self.graph_num += 1
else:
data = Data2D(image=numpy.nan_to_num(self.data_to_plot),
qx_data=self.data.qx_data,
diff --git a/src/sas/qtgui/Calculators/UI/GenericScatteringCalculator.ui b/src/sas/qtgui/Calculators/UI/GenericScatteringCalculator.ui
index 4be51bf20f..ee265caa7f 100644
--- a/src/sas/qtgui/Calculators/UI/GenericScatteringCalculator.ui
+++ b/src/sas/qtgui/Calculators/UI/GenericScatteringCalculator.ui
@@ -36,994 +36,383 @@
:/res/ball.ico:/res/ball.ico
- -
-
-
- Qt::Horizontal
-
-
-
- 3
- 20
-
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- -
-
+
-
+
false
- SLD Data File
+ Coordinate System Info
-
-
-
-
-
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Nuclear data used to simulate SANS.</p></body></html>
-
-
- Nuclear Data
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 151
- 0
-
-
-
-
- false
-
-
-
- Display name of loaded datafile.
-
-
- No File Loaded
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 23
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Only .txt, .sld, .vtk and .pdb datafile formats are supported. </p><p>Load Nuclear sld data.</p></body></html>
-
-
- Load
-
-
- false
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Magnetic data used to simulate SANS.</p></body></html>
-
-
- Magnetic Data
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 151
- 0
-
-
-
-
- false
-
-
-
- Display name of loaded datafile.
-
-
- No File Loaded
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 80
- 23
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Only .txt, .omf, .vtk and .sld datafile formats are supported. </p><p>Load Magnetic sld data.</p></body></html>
-
-
- Load
-
-
- false
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Default shape of the sample.</p></body></html>
-
-
- Shape
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Select the default shape of the sample.</p></body></html>
-
-
-
+
+
-
+
+
+
+ false
+
+
+
+ Environment Coordinates (uvw)
+
+
+
-
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The yaw angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
- Rectangular
+ Yaw
-
-
-
- -
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- 80
- 23
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Generate a 3D plot with arrows for the magnetic vectors.</p><p>It is not recommanded for a large number of pixels.</p></body></html>
-
-
- Draw
-
-
- false
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 30
-
-
-
-
- false
-
-
-
- Verification Error
-
-
-
-
-
- Qt::AlignHCenter
-
-
- true
-
-
-
- -
-
-
-
- false
-
-
-
- SLD Pixel Info
-
-
-
-
-
-
-
- false
-
-
-
- Number of pixels.
-Not editable.
-
-
- No. of Pixels
-
-
-
- -
-
-
- false
-
-
-
- 110
- 27
-
-
-
-
- false
-
-
-
- 1000
-
-
- true
-
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The yaw angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The pitch angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
+
+ Pitch
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The pitch angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The roll angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
+
+ Roll
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The roll angle of the environment coordinates from the beamline coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
+
+
- -
-
+
-
+
false
- Mean SLD
+ Sample Coordinates (xyz)
-
+
-
-
-
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Mean value of M<span style=" vertical-align:sub;">x</span> (x-component of the magnetisation vector).</p></body></html>
-
-
- Mx
-
-
-
- -
-
-
-
- 70
- 18
-
-
-
-
- false
-
-
-
- x component of the magnetization vector in the laboratory xyz frame
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Mean value of My (y-component of the magnetisation vector).</p></body></html>
-
-
- My
-
-
-
- -
-
-
-
- 70
- 18
-
-
-
-
- false
-
-
-
- y component of the magnetization vector in the laboratory xyz frame
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Mean value of M<span style=" vertical-align:sub;">z</span> (z-component of the magnetisation vector).</p></body></html>
-
-
- Mz
-
-
-
- -
-
-
-
- 70
- 18
-
-
-
-
- false
-
-
-
- z component of the magnetization vector in the laboratory xyz frame
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Average of the nuclear scattering density.</p></body></html>
-
-
- Nucl.
-
-
-
- -
-
-
-
- 70
- 18
-
-
-
-
- false
-
-
-
- 6.97e-06
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
-
-
-
-
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The yaw angle of the sample coordinates from the environment coordinates.</p></body></html>
+
+
+ Yaw
+
+
-
-
-
- -
-
-
-
- false
-
-
-
- Nodes
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
- xnodes
-
-
-
- -
-
-
-
- 56
- 18
-
-
-
-
- false
-
-
-
- 10
-
-
-
- -
-
-
-
- false
-
-
-
- ynodes
-
-
-
- -
-
-
-
- 56
- 18
-
-
-
-
- false
-
-
-
- 10
-
-
-
- -
-
-
-
- false
-
-
-
- znodes
-
-
-
- -
-
-
-
- 56
- 18
-
-
-
-
- false
-
-
-
- 10
-
-
-
-
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The yaw angle of the sample coordinates from the environment coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The pitch angle of the sample coordinates from the environment coordinates.</p></body></html>
+
+
+ Pitch
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The pitch angle of the sample coordinates from the environment coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The roll angle of the sample coordinates from the environment coordinates.</p></body></html>
+
+
+ Roll
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>The roll angle of the sample coordinates from the enivronment coordinates.</p></body></html>
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
+
+
- -
-
-
-
- false
-
-
-
- Step Size
-
-
-
-
-
-
-
-
-
-
- false
-
-
-
- xstepsize
-
-
-
- -
-
-
-
- 50
- 18
-
-
-
-
- false
-
-
-
- 6
-
-
-
- -
-
-
-
- false
-
-
-
- Å
-
-
-
- -
-
-
-
- false
-
-
-
- ystepsize
-
-
-
- -
-
-
-
- 50
- 18
-
-
-
-
- false
-
-
-
- 6
-
-
-
- -
-
-
-
- false
-
-
-
- Å
-
-
-
- -
-
-
-
- false
-
-
-
- zstepsize
-
-
-
- -
-
-
-
- 50
- 18
-
-
-
-
- false
-
-
-
- 6
-
-
-
- -
-
-
-
- false
-
-
-
- Å
-
-
-
-
-
-
-
-
- -
-
-
- true
-
-
-
- 0
- 0
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Draw a scatter plot for sld profile (without arrows)</p></body></html>
-
-
- Draw Points
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 7
- 20
-
-
-
-
- -
-
-
- false
-
-
-
- 0
- 0
-
-
-
-
- false
-
-
-
- <html><head/><body><p>Save the sld data as sld format.</p></body></html>
-
-
- Save SLD Data
-
-
- false
-
-
-
- cmdDrawpoints
- cmdSave
- lblNoPixels
- txtNoPixels
- groupBox_5
- groupBox_6
- groupBox_Stepsize
-
-
- -
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 155
- 23
-
-
-
-
- 155
- 26
-
-
-
- <html><head/><body><p>Option of orientation to perform calculations:</p><p>- Scattering calculated for fixed orientation → 2D output</p><p>- Scattering orientation averaged over all orientations → 1D output</p><p>This choice is only available for pdb files.</p></body></html>
-
-
-
-
- Fixed orientation
-
-
- -
-
- Debye full avg.
-
-
- -
-
- Full avg. w/ β(Q)
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 0
- 20
-
-
-
-
- -
-
+
-
+
false
- Q Range
+ SLD Data File
-
+
-
-
-
-
-
+
+
-
+
false
+
+ <html><head/><body><p>Nuclear data used to simulate SANS.</p></body></html>
+
- <html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>
+ Nuclear Data
- -
-
+
-
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
- 0
- 18
+ 151
+ 0
@@ -1031,48 +420,78 @@ Not editable.
false
+
+ Display name of loaded datafile.
+
- 0.3
+ No File Loaded
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 80
+ 23
+
+
false
- <html><head/><body><p>Maximum value of Q<span style=" vertical-align:sub;">x,y</span>.</p><p>Q<span style=" vertical-align:sub;">x,ymax </span>∈ ]0, 1000].</p></body></html>
+ <html><head/><body><p>Only .txt, .sld, .vtk and .pdb datafile formats are supported. </p><p>Load Nuclear sld data.</p></body></html>
- Qx (Qy) Max
+ Load
+
+
+ false
- -
-
+
-
+
false
- Number of bins in reciprocal space for the 1D or 2D plot generated by 'Compute'.
-Number of Qbins ∈ [2, 1000].
+ <html><head/><body><p>Magnetic data used to simulate SANS.</p></body></html>
- No. of Qx (Qy) bins
+ Magnetic Data
- -
-
+
-
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
- 0
- 18
+ 151
+ 0
@@ -1080,206 +499,193 @@ Number of Qbins ∈ [2, 1000].
false
+
+ Display name of loaded datafile.
+
- 30
+ No File Loaded
-
-
-
-
-
- -
-
-
-
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 75
- 23
-
-
-
- Close the calculator.
-
-
- Close
-
-
-
- 17
- 16
-
-
-
- false
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 75
- 23
-
-
-
- Reset the interface to its default values
-
-
- Reset
-
-
- false
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 75
- 23
-
-
-
- Display 'Help' information about the calculator
-
-
- Help
-
-
- false
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- <html><head/><body><p>Compute the scattering pattern and display 1D or 2D plot depending on the settings.</p></body></html>
-
-
- Compute
-
-
- false
-
-
-
-
-
- -
-
-
-
- false
-
-
-
- Input Parameters
-
-
-
-
-
-
- QLayout::SetMinimumSize
-
-
-
-
-
-
- false
-
+
-
+
+
+
+ 0
+ 0
+
-
- Parameter
+
+
+ 80
+ 23
+
-
-
- -
-
false
+
+ <html><head/><body><p>Only .txt, .omf, .vtk and .sld datafile formats are supported. </p><p>Load Magnetic sld data.</p></body></html>
+
- Value
+ Load
+
+
+ false
- -
-
+
-
+
false
+
+ <html><head/><body><p>Default shape of the sample.</p></body></html>
+
- Unit
+ Shape
- -
-
+
-
+
+
+
+ 0
+ 0
+
+
false
- <html><head/><body><p>Ratio of spin up/(spin up + spin down) neutrons after the analyzer.</p><p>It must be between 0 and 1.</p><p>It is equal to 0.5 for unpolarized neutrons.</p><p>The editing is disabled if data are from .omf, .sld, .pdb files.</p></body></html>
-
-
- Up_frac_in
+ <html><head/><body><p>Select the default shape of the sample.</p></body></html>
+
-
+
+ Rectangular
+
+
- -
-
+
-
+
+
+ true
+
+
+
+ 0
+ 0
+
+
- 0
- 18
+ 80
+ 23
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Generate a 3D plot with arrows for the magnetic vectors.</p><p>It is not recommanded for a large number of pixels.</p></body></html>
+
+
+ Draw
+
+
+ false
+
+
+
+
+
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Input Parameters
+
+
+
-
+
+
+ QLayout::SetMinimumSize
+
+
-
+
+
+
+ false
+
+
+
+ Parameter
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Value
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Unit
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Ratio of spin up/(spin up + spin down) neutrons after the analyzer.</p><p>It must be between 0 and 1.</p><p>It is equal to 0.5 for unpolarized neutrons.</p><p>The editing is disabled if data are from .omf, .sld, .pdb files.</p></body></html>
+
+
+ Up_frac_in
+
+
+
+ -
+
+
+
+ 0
+ 18
@@ -1621,337 +1027,942 @@ Number of Qbins ∈ [2, 1000].
- -
-
+
-
+
false
- Coordinate System Info
+ SLD Pixel Info
-
-
-
-
+
+
-
+
+
+
+ false
+
+
+
+ Number of pixels.
+Not editable.
+
+
+ No. of Pixels
+
+
+
+ -
+
+
+ false
+
+
+
+ 110
+ 27
+
+
+
+
+ false
+
+
+
+ 1000
+
+
+ true
+
+
+
+ -
+
false
- Environment Coordinates (uvw)
+ Mean SLD
-
+
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The yaw angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- Yaw
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The yaw angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>The pitch angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- Pitch
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The pitch angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>The roll angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- Roll
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The roll angle of the environment coordinates from the beamline coordinates.</p></body></html>
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
-
-
-
-
- -
-
-
-
- false
-
-
-
- Sample Coordinates (xyz)
-
-
-
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The yaw angle of the sample coordinates from the environment coordinates.</p></body></html>
-
-
- Yaw
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The yaw angle of the sample coordinates from the environment coordinates.</p></body></html>
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>The pitch angle of the sample coordinates from the environment coordinates.</p></body></html>
-
-
- Pitch
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The pitch angle of the sample coordinates from the environment coordinates.</p></body></html>
-
-
- 0.0
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
-
- -
-
-
-
- false
-
-
-
- <html><head/><body><p>The roll angle of the sample coordinates from the environment coordinates.</p></body></html>
-
-
- Roll
-
-
-
- -
-
-
-
- 0
- 18
-
-
-
-
- false
-
-
-
- <html><head/><body><p>The roll angle of the sample coordinates from the enivronment coordinates.</p></body></html>
-
-
- 0.0
-
-
+
+
-
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Mean value of M<span style=" vertical-align:sub;">x</span> (x-component of the magnetisation vector).</p></body></html>
+
+
+ Mx
+
+
+
+ -
+
+
+
+ 70
+ 18
+
+
+
+
+ false
+
+
+
+ x component of the magnetization vector in the laboratory xyz frame
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Mean value of My (y-component of the magnetisation vector).</p></body></html>
+
+
+ My
+
+
+
+ -
+
+
+
+ 70
+ 18
+
+
+
+
+ false
+
+
+
+ y component of the magnetization vector in the laboratory xyz frame
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Mean value of M<span style=" vertical-align:sub;">z</span> (z-component of the magnetisation vector).</p></body></html>
+
+
+ Mz
+
+
+
+ -
+
+
+
+ 70
+ 18
+
+
+
+
+ false
+
+
+
+ z component of the magnetization vector in the laboratory xyz frame
+
+
+ 0.0
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Average of the nuclear scattering density.</p></body></html>
+
+
+ Nucl.
+
+
+
+ -
+
+
+
+ 70
+ 18
+
+
+
+
+ false
+
+
+
+ 6.97e-06
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-2</span></p></body></html>
+
+
+
+
- -
-
-
-
- false
-
-
-
- <html><head/><body><p><span style=" vertical-align:super;">o</span></p></body></html>
-
-
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Nodes
+
+
+
-
+
+
-
+
+
+
+ false
+
+
+
+ xnodes
+
+
+
+ -
+
+
+
+ 56
+ 18
+
+
+
+
+ false
+
+
+
+ 10
+
+
+
+ -
+
+
+
+ false
+
+
+
+ ynodes
+
+
+
+ -
+
+
+
+ 56
+ 18
+
+
+
+
+ false
+
+
+
+ 10
+
+
+
+ -
+
+
+
+ false
+
+
+
+ znodes
+
+
+
+ -
+
+
+
+ 56
+ 18
+
+
+
+
+ false
+
+
+
+ 10
+
+
+
+
+
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Step Size
+
+
+
-
+
+
-
+
+
+
+ false
+
+
+
+ xstepsize
+
+
+
+ -
+
+
+
+ 50
+ 18
+
+
+
+
+ false
+
+
+
+ 6
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Å
+
+
+
+ -
+
+
+
+ false
+
+
+
+ ystepsize
+
+
+
+ -
+
+
+
+ 50
+ 18
+
+
+
+
+ false
+
+
+
+ 6
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Å
+
+
+
+ -
+
+
+
+ false
+
+
+
+ zstepsize
+
+
+
+ -
+
+
+
+ 50
+ 18
+
+
+
+
+ false
+
+
+
+ 6
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Å
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 0
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Draw a scatter plot for sld profile (without arrows)</p></body></html>
+
+
+ Draw Points
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 7
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Save the sld data as sld format.</p></body></html>
+
+
+ Save SLD Data
+
+
+ false
+
+
+
+
+ cmdDrawpoints
+ cmdSave
+ lblNoPixels
+ txtNoPixels
+ groupBox_5
+ groupBox_6
+ groupBox_Stepsize
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 75
+ 23
+
+
+
+ Close the calculator.
+
+
+ Close
+
+
+
+ 17
+ 16
+
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 75
+ 23
+
+
+
+ Reset the interface to its default values
+
+
+ Reset
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 75
+ 23
+
+
+
+ Display 'Help' information about the calculator
+
+
+ Help
+
+
+ false
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>Compute the scattering pattern and display 1D or 2D plot depending on the settings.</p></body></html>
+
+
+ Compute
+
+
+ false
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 3
+ 20
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+
+ 0
+ 30
+
+
+
+
+ false
+
+
+
+ Verification Error
+
+
+
+
+
+ Qt::AlignHCenter
+
+
+ true
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Q Range
+
+
+
-
+
+
-
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ 0.3
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>
+
+
+
+ -
+
+
+
+ false
+
+
+
+ <html><head/><body><p>Maximum value of Q<span style=" vertical-align:sub;">x,y</span>.</p><p>Q<span style=" vertical-align:sub;">x,ymax </span>∈ ]0, 1000].</p></body></html>
+
+
+ Qx (Qy) Max
+
+
+
+ -
+
+
+
+ false
+
+
+
+ Number of bins in reciprocal space for the 1D or 2D plot generated by 'Compute'.
+Number of Qbins ∈ [2, 1000].
+
+
+ No. of Qx (Qy) bins
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ 30
+
+
+
+ -
+
+
+ <html><head/><body><p>Minimum value of Q<span style=" vertical-align:sub;">x,y </span></p><p>Default Values- 2D: [-1 * QMax]; 1D: [.001 * QMax]; </p><p>Q<span style=" vertical-align:sub;">x,ymin </span>∈ ]0, 1000].</p></body></html>
+
+
+ Qx (Qy) Min
+
+
+
+ -
+
+
+
+ 0
+ 18
+
+
+
+
+ false
+
+
+
+ -0.3
+
+
+ 3
+
+
+
+ -
+
+
+ <html><head/><body><p>Å<span style=" vertical-align:super;">-1</span></p></body></html>
+
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Checking this box will use Log Spacing between the Qx Values rather than Linear Spacing.</p></body></html>
+
+
+ Log Spacing
+
+
+
- -
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 20
+
+
+
+
+ -
Qt::Horizontal
@@ -1964,27 +1975,147 @@ Number of Qbins ∈ [2, 1000].
- -
-
-
-
-
-
- Radius of Gyration
-
-
-
- -
-
-
- false
+
-
+
+
+
+ 180
+ 16777215
+
+
+
+ Radius of Gyration
+
+
+
-
+
+
+ Radius of Gyration, calculated with the mass of each atom.
+
+
+ Rg - Mass
+
+
+
+ -
+
+
+ false
+
+
+ 0
+
+
+
+ -
+
+
+ -
+
+
+ false
+
+
+ 0
+
+
+
+ -
+
+
+ Guinier Radius - Radius of Gyration based on Scattering Length Density
+NOTE: Currently not impacted by Solvent SLD
+
+
+ RG - SLD
+
+
+
+
+
+
+ -
+
+
+ QLayout::SetMaximumSize
+
+
-
+
+
+
+ 210
+ 0
+
-
- 0
+
+ Plugin Models
+
+
-
+
+
+ Export Model
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 18
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 23
+
+
+
+
+ 210
+ 26
+
+
+
+ <html><head/><body><p>Option of orientation to perform calculations:</p><p>- Scattering calculated for fixed orientation → 2D output</p><p>- Scattering orientation averaged over all orientations → 1D output</p><p>This choice is only available for pdb files.</p></body></html>
+
+
-
+
+ Fixed orientation
+
+
+ -
+
+ Debye full avg.
+
+
+ -
+
+ Debye full avg. w/ β(Q)
+
+
+
+
@@ -2006,7 +2137,6 @@ Number of Qbins ∈ [2, 1000].
txtTotalVolume
txtNoQBins
txtQxMax
- cbOptionsCalc
txtNoPixels
txtMx
txtMy
diff --git a/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index.jpg b/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index.jpg
new file mode 100644
index 0000000000..5849d5fd24
Binary files /dev/null and b/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index.jpg differ
diff --git a/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index2.jpg b/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index2.jpg
new file mode 100644
index 0000000000..c9480306c8
Binary files /dev/null and b/src/sas/qtgui/Calculators/media/GSC_Oct282023_GUI_Index2.jpg differ
diff --git a/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data.rst b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data.rst
new file mode 100644
index 0000000000..3b52b6ff20
--- /dev/null
+++ b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data.rst
@@ -0,0 +1,57 @@
+.. gsc_ex_customModel_data.rst
+
+.. _gsc_ex_customModel_data:
+
+Example: Fit the experimental data using the calculated $P(Q)$ from a PDB file
+==================================
+
+(Pictures in this document were generated using SasView 6.0.0.) To calculate $P(Q)$ from a PDB file, use the Generic Scattering Calculator in the Tools menu. In this example, a PDB file for apoferritin was downloaded from the PDB data bank (https://www.rcsb.org/structure/6z6u). The custom model function, custom_apoferritinLong, was obtained from the scattering calculator using the Debye full avg. w/ $\beta(Q)$ option after loading the apoferritin PDB file (see print-screen below). Note that the q-range and the number of data points can be customized. The Export Model box should be checked to enable a file name for the custom plugin model that will be produced (custom_apoferritinLong). Clicking "Compute" launches the calculation of $P(Q)$ and $\beta(Q)$, and generate the file into the plugin model directory of the SasView. Once the computation is finished, the plugin model is ready to use to fit scattering data.
+
+.. figure:: gsc_ex_customModel_data_Fig3.jpg
+
+To use the calculated custom apoferritinLong model in Fit panel, one selects "Plugin Models" in the Category box. The custom model should already exist in the SasView directory and can be found in the drop down menu of the "Model name". This custom model, custom_apoferritinLong, returns both the normalized form factor, $\tilde{P}(Q)$, and $\beta(Q)$ caculated using the PDB file. But $\beta(Q)$ is only used if one needs to fit the data with the inter-particle structure factor, $S(Q)$, with the static decoupling approximation.
+
+Warning: when generating the custom plugin model using the Generic Scattering Calculator, please be careful about the following two issues before using it:
+
+1) There should be $enough$ number of data points included in the plugin model. Because the plugin model calculates the theoretical $I(Q)$ by interpolating the data during the fitting, the more data points generated by the Generic Scattering Calculator, the more accurate the theoretical curves is.
+
+2) Because each instrument has a resolution function for each $Q$ value, the theoretical curve needs to be calculated by considering the resolution function. To make the fitting function working properly, the $Q$ range calculated for the plugin model in the Generic Scattering Calculator should be larger than the $Q$ range of the experimental data. If the $Q$ range of theoretical data is too close to (or smaller than) that of the experimental data, the calculated theoretical values may all return as "NaN". When this happens, the plugin model will not function properly to fit experimental data.
+
+The following example is to demonstrate how to fit the data with the calculated form factor.
+
+.. figure:: gsc_ex_customModel_data_Fig1.jpg
+
+The scattering pattern, $I(Q)$, is calculated as
+
+.. math::
+ I(Q) = \frac{scale}{V}(SLD - SLD_{solvent})^2V_{protein} \tilde{P}(Q\alpha)S_{eff}(Q) + background
+
+$SLD$ is the scattering length density of the protein (or particle). And $SLD\_solvent$ is the scattering length density of solvent.
+
+$V_{protein}$ is the protein volume. If the scattering length density and protein volume are assigned with correct values, $scale$ is the volume fraction of the protein ( or particle).
+
+$\alpha$ is the swelling factor. In general, $\alpha$ should be one (or close to one). The parameter is introduced just in case that there is a slight swelling of the particle.
+
+$S_{eff}(Q)$ is the effective structure factor with
+
+.. math::
+ S_{eff}(Q) = 1 + \beta(Q\alpha)(S(Q)+1)
+
+And $S(Q)$ is the interparticule structure factor.
+
+For dilute solutions, it is simplified as
+
+.. math::
+ I(Q) = \frac{scale}{V}(SLD - SLD_{solvent})^2V_{protein} \tilde{P}(Q\alpha) + background
+
+The following figure shows the comparison between one experimental apoferritin protein SANS data of a dilute sample with the calculated $I(Q)$ using this model.
+
+.. figure:: gsc_ex_customModel_data_Fig2.jpg
+
+If one needs to fit concentrated protein solutions, an appropriate structure factor model needs to be chosen. If a protein is anisotropic, one also needs to use the static decoupling approximation. $\beta(Q)$ will be automatially used during the fitting. One fitting parameter to calculate $S(Q)$ using the models available in the SasView is the effective radius that is called radius\_effective in the SasView. One needs to choose how to correlate the effective radius with the size of a protein. Note that the effective radius is related with the interaciton between porteins, and could be different from the radius of a protein/particle. It is therefore ok, and sometimes recommended, to use unconstrained method for the "radius\_effective_mode" since there is no fitting prameter related with the protein size in this custom fitting model. If a protein is nearly spherical such as apoferritin, there is no need to use the static decoupling approximation.
+
+
+*Document History*
+
+| 2023-10-30 Yun Liu
+
diff --git a/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig1.jpg b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig1.jpg
new file mode 100644
index 0000000000..175151592a
Binary files /dev/null and b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig1.jpg differ
diff --git a/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig2.jpg b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig2.jpg
new file mode 100644
index 0000000000..87f70a4f54
Binary files /dev/null and b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig2.jpg differ
diff --git a/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig3.jpg b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig3.jpg
new file mode 100644
index 0000000000..96e076f313
Binary files /dev/null and b/src/sas/qtgui/Calculators/media/gsc_ex_customModel_data_Fig3.jpg differ
diff --git a/src/sas/qtgui/Calculators/media/sas_calculator_help.rst b/src/sas/qtgui/Calculators/media/sas_calculator_help.rst
index fb7b62ba92..64c8a8eeb0 100755
--- a/src/sas/qtgui/Calculators/media/sas_calculator_help.rst
+++ b/src/sas/qtgui/Calculators/media/sas_calculator_help.rst
@@ -5,7 +5,7 @@
.. _SANS_Calculator_Tool:
-Generic SANS Calculator Tool
+Generic SAS Calculator Tool
============================
Description
@@ -41,7 +41,7 @@ data), in a variety of shapes, such as tetrahedra, cubes or hexahedra.
The scattering length density (SLD) is assumed uniform for each pixel or
element. Depending on the data format the property is either nuclear (in units
-of 10\ :sup:`-6`\ |Ang|:sup:`-2`) (`PDB `_ file) or
+of |Ang|:sup:`-2`) (`PDB `_ file) or
magnetic SLDs (`OMF `_ file) or a combination of both
(`SLD `_ and `VTK `_ files). For magnetic neutron
scattering, the :ref:`magnetism` documentation gives further details and
@@ -59,9 +59,9 @@ discretized with $N$ 3-dimensional rectangular pixels.
The elastic scattering intensity is defined as
.. math::
- I(\mathbf{Q}) = \frac{1}{V}\left\lvert\sum_j^Nv_j\beta_j\exp(i\mathbf{Q}\cdot\mathbf{r_j})\right\rvert^2
+ I(\mathbf{Q}) = \frac{1}{V}\left\lvert\sum_j^Nv_j\rho_j\exp(i\mathbf{Q}\cdot\mathbf{r_j})\right\rvert^2
-where $\beta_j$ and $\mathbf{r}_j$ are the scattering length density and
+where $\rho_j$ and $\mathbf{r}_j$ are the scattering length density and
the position of the $j^\text{th}$ pixel respectively.
The total volume $V_s$ of structures different than the homogenous media is
@@ -71,9 +71,9 @@ equal to
V_s = \sum_j^N v_j
-for $\beta_j \ne 0$ where $v_j$ is the volume of the $j^\text{th}$
+for $\rho_j \ne 0$ where $v_j$ is the volume of the $j^\text{th}$
pixel or natural atomic volume (for .pdb). For atomic structures
-$v_j \beta_j \equiv b_j$ is the scattering length of the $j^\text{th}$ atom and
+$v_j \rho_j \equiv b_j$ is the scattering length of the $j^\text{th}$ atom and
the natural atomic volume is given by:
$\frac{\text{atomic mass}}{\text{natural molar density}\times\text{Avogadro number}}$
@@ -86,11 +86,11 @@ For non-magnetic, grid-type data the 1D orientationally averaged scatting intens
can also be calculated using the *Debye full average* option which uses the Debye formula:
.. math::
- I(\left\lvert\mathbf{Q}\right\rvert) = \frac{1}{V}\sum_j^N v_j\beta_j \sum_k^N v_k\beta_k
+ I(\left\lvert\mathbf{Q}\right\rvert) = \frac{1}{V}\sum_j^N v_j\rho_j \sum_k^N v_k\rho_k
\frac{\sin\left(\left\lvert\mathbf{Q}\right\rvert\left\lvert\mathbf{r_j}-\mathbf{r_k}\right\rvert\right)}
{\left\lvert\mathbf{Q}\right\rvert\left\lvert\mathbf{r_j}-\mathbf{r_k}\right\rvert}
-*NOTE:* $\beta_j$ *displayed in the GUI may be incorrect (input
+*NOTE:* $\rho_j$ *displayed in the GUI may be incorrect (input
parameter* solvent_SLD *) but this will not affect the scattering computation if
the correction of the total volume V is made.*
@@ -104,15 +104,15 @@ For example this cube is formed of five finite elements:
:align: center
Each element has an associated scattering length
-density ($\beta_j$) for the occupied space $V_j$ and the elastic scattering
+density ($\rho_j$) for the occupied space $V_j$ and the elastic scattering
intensity is calculated as
.. math::
- I(\mathbf{Q}) = \frac{1}{V}\left\lvert\sum_j^N\beta_j\iiint\limits_{V_j}\exp(i\mathbf{Q}\cdot\mathbf{r_j})\text{d}V\right\rvert^2
+ I(\mathbf{Q}) = \frac{1}{V}\left\lvert\sum_j^N\rho_j\iiint\limits_{V_j}\exp(i\mathbf{Q}\cdot\mathbf{r_j})\text{d}V\right\rvert^2
Note that the Fourier transform is calculated over each element - allowing
-regions of space with little variation in $\beta$ to have larger finite
+regions of space with little variation in $\rho$ to have larger finite
elements, and regions of interest to have much smaller finite elements, and
hence more detail.
@@ -175,7 +175,7 @@ How to use the Tool
-------------------
Upon loading the calculator we are shown the following interface:
-.. figure:: gen_gui_help.png
+.. figure:: GSC_Oct282023_GUI_Index2.jpg
:align: center
..
@@ -229,6 +229,7 @@ Inputs
In some circumstances these textboxes will be highlighted orange, as a
warning that with the values chosen numerical artefacts may appear due to
the Nyquist criterion, or simulation box size.
+ * When calculating 1D data, Q values are evenly spaced in the log scale if "Log Spacing" box is checked.
Information Panel
^^^^^^^^^^^^^^^^^
@@ -259,14 +260,17 @@ Information Panel
and the mousewheel used to zoom in and out.
19) This choice appears only for grid type data and without magnetic SLD.
This tool allows to either compute the fully oriented 2D scattering pattern,
- or calculating the 1D orientational averaged intensity $I(Q)$ by the Debye
- equation.
+ or calculating the 1D orientational averaged intensity $I(Q)$ by the Debye equation. One can also choose a compuation option, Debye full avg. w/ $\beta(Q)$, to calculate 1D scattering pattern together with $\beta(Q)$. $\beta(Q)$ is needed when fitting scattering patterns of concentrated solutions using the inter-particle structure factor, $S(Q)$, with the static decoupling approximation.
20) Starts the computation of the scattering pattern.
21) Reset GUI to the initial state.
-
-
-As an example :ref:`here ` you can find a simple demonstration of
-the functionality of the Generic scattering calculator using the default
+ 22) If a PDB file is loaded, the radius of gyration is calculated and displayed. "Rg-MASS" is the radius of gyration based on the mass of all atoms in a molecule. "RG-SLD" is the radius of gyration based on the scattering length of all atoms.
+ 23) If the option, Debye full avg. w/ $\beta(Q)$, is chosen, one has the option to check the box "Export Model". Once checked, one can input a file name in the box below. During the computation, the program then exports the calculated normalized form factor, $P(Q)$, and $\beta(Q)$ into this file that automatically become a model in the "Plugin Models". The model name is the same as the file name given in the blox below "Export Model".
+
+One example is given here ( Click :ref:`here ` ) to illustrate how to calculate $P(Q)$ and $\beta(Q)$ using a PDB file of a protein. These are 1D functions after averaging over all orientiations of proteins. The program can generate a custom model function, which can be used to fit the 1D small angle scattering data.
+
+
+One other example ( Click :ref:`here ` ) is a simple demonstration of
+the functionality of the Generic scattering calculator to calculate the 2D scattering pattern using the default
starting values with no files loaded.
After computation the result will appear in the
@@ -370,9 +374,9 @@ the SLD at the centre of each element. This weighted average is given by:
.. math::
- \bar{\beta} = \frac{\sum\limits_j^n \beta_j r_j^{\prime -2}}{\sum\limits_j^nr_j^{\prime -2}}
+ \bar{\rho} = \frac{\sum\limits_j^n \rho_j r_j^{\prime -2}}{\sum\limits_j^nr_j^{\prime -2}}
-Where $\bar{\beta}$ is the estimated SLD for an element and $\beta_j$, $r'_j$
+Where $\bar{\rho}$ is the estimated SLD for an element and $\rho_j$, $r'_j$
are the SLDs and distances from the centre of the element of each of the $n$
vertices of the element respectively. $r'_j$ is taken as:
@@ -638,3 +642,4 @@ References
| 2015-05-01 Steve King
| 2021-09-14 Robert Bourne
+| 2023-10-30 Yun Liu
diff --git a/src/sas/sascalc/calculator/geni.py b/src/sas/sascalc/calculator/geni.py
index f16710e153..b1b9e15d21 100644
--- a/src/sas/sascalc/calculator/geni.py
+++ b/src/sas/sascalc/calculator/geni.py
@@ -5,6 +5,12 @@
import os
import logging
import numpy as np
+import periodictable
+
+from typing import Union
+
+from sas.sascalc.calculator.sas_gen import MagSLD, OMF2SLD
+
try:
if os.environ.get('SAS_NUMBA', '1').lower() in ('1', 'yes', 'true', 't'):
@@ -475,4 +481,124 @@ def _spin_weights(in_spin, out_spin):
in_spin * (1.0-out_spin) / norm, # ud
in_spin * out_spin / norm, # uu
)
- return weight
\ No newline at end of file
+ return weight
+
+
+def radius_of_gyration(nuc_sl_data: Union[MagSLD, OMF2SLD]) -> tuple[str, str, float]:
+ """Calculate parameters related to the radius of gyration using and SLD profile.
+
+ :param nuc_sl_data: A scattering length object for a series of atomic points in space
+ :return: A tuple of the string representation of the radius of gyration, Guinier slope, and Rg as a float.
+ """
+ # Calculate Center of Mass First
+ c_o_m = center_of_mass(nuc_sl_data)
+
+ x = nuc_sl_data.pos_x
+ y = nuc_sl_data.pos_y
+ z = nuc_sl_data.pos_z
+ pix_symbol = nuc_sl_data.pix_symbol
+ coordinates = np.array([x, y, z]).T
+ coherent_sls, masses = np.empty(len(pix_symbol)), np.empty(len(pix_symbol))
+ for i, sym in enumerate(pix_symbol):
+ atom = periodictable.elements.symbol(sym)
+ masses[i] = atom.mass
+ coherent_sls[i] = atom.neutron.b_c
+ # solvent_slds = atoms.volume() * 10**24 * float(self.txtSolventSLD.text()) * 10**5
+
+ # TODO: Implement a scientifically sound method for obtaining protein volume - Current value is a imprecise
+ # approximation. Until then Solvent SLD does not impact RG - SLD.
+ # This method only calculates RG of proteins in vacuum. Implementing the RG calcuation in solvent needs
+ # the input of the solvent volume.
+ contrast_sls = coherent_sls # femtometer
+ rsq = np.sum((c_o_m - coordinates)**2, axis=1)
+
+ rog_num = np.sum(masses * rsq)
+ rog_den = np.sum(masses)
+ guinier_num = np.sum(contrast_sls * rsq)
+ guinier_den = np.sum(contrast_sls)
+
+ if rog_den <= 0: #Should never happen as there are no zero or negative mass atoms
+ rog_mass = "NaN"
+ r_g_mass = 0.0
+ logging.warning("Atomic Mass is zero for all atoms in the system.")
+ else:
+ r_g_mass = np.sqrt(rog_num/rog_den)
+ rog_mass = (str(round(np.sqrt(rog_num/rog_den),1)) + " Å")
+
+ #Avoid division by zero - May occur through contrast matching
+ if guinier_den == 0:
+ guinier_value = "NaN"
+ logging.warning("Effective Coherent Scattering Length is zero for all atoms in the system.")
+ elif (guinier_num/guinier_den) < 0:
+ guinier_value = (str(round(np.sqrt(-guinier_num/guinier_den), 1)) + " Å")
+ logging.warning("Radius of Gyration Squared is negative. R(G)^2 is assumed to be |R(G)|* R(G).")
+ else:
+ guinier_value = (str(round(np.sqrt(guinier_num/guinier_den), 1)) + " Å")
+
+ return rog_mass, guinier_value, r_g_mass # (String, String, Float), float used for plugin model
+
+
+def center_of_mass(nuc_sl_data: Union[MagSLD, OMF2SLD]) -> list[float]:
+ """Calculate Center of Mass(CoM) of provided molecule using an SL profile
+
+ :param nuc_sl_data: A coordinate data object (MagSLD or OMF2SLD)
+ :return: A list of the calculated spatial center of mass, given as cartesian coordinates."""
+ masses = np.asarray([0.0, 0.0, 0.0])
+ densities = np.asarray([0.0, 0.0, 0.0])
+
+ # Only call periodictable once per element -> minimizes calculation time
+ coh_b_storage = {}
+
+ for i in range(len(nuc_sl_data.pos_x)):
+ coordinates = np.asarray([float(nuc_sl_data.pos_x[i]), float(nuc_sl_data.pos_y[i]), float(nuc_sl_data.pos_z[i])])
+
+ #Coh b - Coherent Scattering Length(fm)
+ symbol = nuc_sl_data.pix_symbol[i]
+ coh_b = coh_b_storage.get(symbol, periodictable.elements.symbol(symbol).neutron.b_c)
+ coh_b_storage[symbol] = coh_b
+
+ masses += (coordinates*coh_b)
+ densities += coh_b
+
+ c_o_m = np.divide(masses, densities)
+
+ return c_o_m
+
+
+def create_beta_plot(q_x: np.ndarray, nuc_sl_data: Union[MagSLD, OMF2SLD], form_factor: np.ndarray) -> np.ndarray:
+ """Carry out the computation of beta Q using provided & calculated data
+
+ :param q_x: The Q values where the beta will be calculated.
+ :param nuc_sl_data: A coordinate data object (MagSLD or OMF2SLD)
+ :param form_factor: The form factor calculated prior to applying the beta approximation.
+ :return: An array of form factor values with the beta approximation applied."""
+ f_q = f_of_q(q_x, nuc_sl_data)
+
+ # Center Of Mass Calculation
+ data_beta_q = (f_q**2) / form_factor
+
+ # Scale Beta Q to 0-1
+ scaling_factor = data_beta_q[0]
+ data_beta_q = data_beta_q / scaling_factor
+
+ return data_beta_q
+
+
+def f_of_q(q_x: np.ndarray, nuc_sl_data: Union[MagSLD, OMF2SLD]) -> np.ndarray:
+ """Compute the base F(Q) calculation based from the nuclear data.
+
+ :param q_x: The Q values where the beta will be calculated.
+ :param nuc_sl_data: A coordinate data object (MagSLD or OMF2SLD)
+ :return: An array of form factor data."""
+ c_o_m = center_of_mass(nuc_sl_data)
+ r_x = np.subtract(nuc_sl_data.pos_x, c_o_m[0])
+ r_y = np.subtract(nuc_sl_data.pos_y, c_o_m[1])
+ r_z = np.subtract(nuc_sl_data.pos_z, c_o_m[2])
+ magnitude_relative_coordinate = np.sqrt(np.power(r_x, 2) + np.power(r_y, 2) + np.power(r_z, 2))
+ coh_b = np.asarray([periodictable.elements.symbol(atom).neutron.b_c for atom in nuc_sl_data.pix_symbol])
+
+ f_of_q_list = [np.sum(coh_b * np.sinc(q_x[i] * magnitude_relative_coordinate / np.pi)) for i in range(len(q_x))]
+ f_of_q_list = np.asarray(f_of_q_list) / abs(np.sum(coh_b)) # normalization
+ return f_of_q_list
+
+
diff --git a/src/sas/sascalc/calculator/gsc_model.py b/src/sas/sascalc/calculator/gsc_model.py
new file mode 100644
index 0000000000..b69dcd000e
--- /dev/null
+++ b/src/sas/sascalc/calculator/gsc_model.py
@@ -0,0 +1,157 @@
+"""
+create plugin model from the Generic Scattering Calculator
+"""
+import logging
+import math
+from pathlib import Path
+
+import numpy as np
+
+from sas.sascalc.fit import models
+
+
+def generate_plugin(f_name: str, data_to_plot: np.ndarray, x_values: np.ndarray, f_q: list,
+ mass: float) -> tuple[str, Path]:
+ """Generate an empirical plugin model using calculated data.
+
+ :param f_name: The desired file name for the resulting model
+ :param data_to_plot: The plottable data
+ :param x_values: The x values, as a numpy array
+ :param f_q: The calculated F(Q)
+ :param mass: The mass associated with the Rg calculation
+ """
+ # check if file exists & assign filename
+ plugin_location = Path(models.find_plugins_dir())
+ if not f_name.endswith('.py'):
+ f_name += '.py'
+ full_path = plugin_location / f_name
+
+ # generate the model representation as a string
+ model_str = generate_model(f_name, data_to_plot, x_values, f_q, mass)
+
+ return model_str, full_path
+
+
+def generate_model(f_name: str, data_to_plot: np.ndarray, x_values: np.ndarray, f_q: list, mass: float) -> str:
+ """Generate an empirical model from the current plugin state
+ """
+
+ # TODO:
+ # This should be the correct normalization, but P(Q) has already been rescaled in a different part of the code
+ # pix_symbol = self.nuc_sld_data.pix_symbol
+ # sld = 0
+ # for i, sym in enumerate(pix_symbol):
+ # atom = periodictable.elements.symbol(sym)
+ # sld += atom.neutron.b_c
+ # normPQ = self.data_to_plot / (sld**2)
+ norm_pq = data_to_plot/data_to_plot[0] # temporary fix
+
+ nq = len(x_values)
+ log_q = "{" + ','.join(f'{math.log(v):.15e}' for v in x_values.tolist()) + "}"
+ f_q = "{" + ','.join(f'{v:.15e}' for v in f_q) + "}"
+ log_fq_sq_avg = "{" + ','.join(f'{math.log(v):.15e}' for v in norm_pq) + "}"
+ prefactor = 1e-2
+
+ model_str = (f'''
+r"""
+Example empirical model using interp.
+"""
+# Note: requires the pr-python-fq branch which may or may not have been merged.
+
+import numpy as np
+from numpy import inf
+from types import SimpleNamespace as dotted
+
+
+name = "{f_name.replace('.py', '')}"
+title = "Model precalculated from PDB file."
+description = """
+Interpolate F(q) values from an interpolation table generated for the PDB
+file {{pdbfile}}.pdb.
+"""
+#category = "shape:pdb"
+
+# ["name", "units", default, [lower, upper], "type","description"],
+parameters = [
+ ["sld", "1e-6/Ang^2", 1, [0, inf], "sld", "Protein scattering length density"],
+ ["sld_solvent", "1e-6/Ang^2", 0, [-inf, inf], "sld", "Solvent scattering length density"],
+ ["swelling", "", 1, [0, inf], "volume", "swelling parameter changing effective radius"],
+ ["protein_volume", "Ang^3", 1, [0, inf], "volume", ""],
+]
+
+c_code = r"""
+#define NQ {nq}
+constant double LOGQ[NQ] = {log_q};
+constant double FQ[NQ] = {f_q};
+constant double LOGFQSQ[NQ] = {log_fq_sq_avg};
+constant double prefactor = {prefactor};
+
+static double
+form_volume(double swelling, double protein_volume)
+{{
+ return protein_volume;
+}}
+
+static double
+radius_effective(int mode, double swelling,double protein_volume)
+{{
+ switch (mode) {{
+ default:
+ case 1: // equivalent sphere
+ return (cbrt(protein_volume*3.0/4.0/M_PI))*swelling;
+ case 2: // radius of gyration
+ return {mass}*swelling;
+ }}
+}}
+
+static void
+Fq(double q, double *f1, double *f2, double sld, double sld_solvent, double swelling, double protein_volume)
+{{
+ const double logq = log(q*swelling);
+ if (logq < LOGQ[0] || logq > LOGQ[NQ-1]) {{
+ *f1 = *f2 = NAN;
+ return;
+ }}
+
+ const double contrast = (sld - sld_solvent);
+ const double scale = prefactor * contrast * form_volume(swelling, protein_volume);
+
+ // binary search
+ int steps = 0;
+ const int max_steps = (int)(ceil(log((double)(NQ))/log(2.0)));
+
+ int high = NQ-1;
+ int low = 0;
+ while (low < high-1) {{
+ int mid = (high + low) / 2;
+ if (logq < LOGQ[mid]) {{
+ high = mid;
+ }} else {{
+ low = mid;
+ }}
+//printf("q: %g in [%d, %d] (%g <= %g <= %g)\\n",q,low,high,LOGQ[low],logq,LOGQ[high]);
+ if (steps++ > max_steps) {{
+ printf("Binary search failed for q=%g\\n", q);
+ *f1 = *f2 = NAN;
+ return;
+ }}
+ }}
+ //high = low+1;
+ // linear interpolation
+ const double x1 = LOGQ[low];
+ const double x2 = LOGQ[high];
+ const double frac = (logq - x1)/(x2-x1);
+ *f1 = scale*(FQ[low]*(1-frac) + FQ[high]*frac);
+ *f2 = scale*scale*exp(LOGFQSQ[low]*(1-frac) + LOGFQSQ[high]*frac);
+//printf("scale: %g\\n", scale);
+//printf("Done with q: %g in [%d, %d] (%g <= %g <= %g)\\n",q,low,high,x1,logq, x2);
+//printf("Frac: %g of interval [%g, %g] gives %g\\n", frac, LOGFQSQ[low],LOGFQSQ[high],LOGFQSQ[low]*(1-frac) + LOGFQSQ[high]*frac);
+}}
+"""
+#print(c_code)
+
+have_Fq = True
+radius_effective_modes = ["equivalent sphere", "radius of gyration"]
+''').lstrip().rstrip()
+
+ return model_str