syncmoss 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.gitignore +10 -0
- {syncmoss-0.2.0/syncmoss.egg-info → syncmoss-0.2.2}/PKG-INFO +1 -1
- {syncmoss-0.2.0 → syncmoss-0.2.2}/bundle/MacOS.spec +47 -17
- {syncmoss-0.2.0 → syncmoss-0.2.2}/bundle/Windows.spec +32 -12
- {syncmoss-0.2.0 → syncmoss-0.2.2}/bundle/bundle.py +1 -2
- {syncmoss-0.2.0 → syncmoss-0.2.2}/bundle/macos_bundle.py +1 -1
- {syncmoss-0.2.0 → syncmoss-0.2.2}/pyproject.toml +2 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Calibration.py +3 -2
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/fitting_io.py +117 -118
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/instrumental_io.py +241 -33
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/main.py +2 -1
- syncmoss-0.2.2/syncmoss/minimi_lib.py +676 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/model_io.py +63 -9
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/models.py +20 -3
- syncmoss-0.2.2/syncmoss/parameters/Calibration.dat +514 -0
- syncmoss-0.2.2/syncmoss/parameters/INSexp.txt +1 -0
- syncmoss-0.2.2/syncmoss/parameters/INSint.txt +1 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters_table.py +59 -3
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/results_table.py +2 -2
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/spectrum_io.py +38 -35
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/spectrum_plotter.py +1 -1
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/syncmoss_main.py +275 -109
- {syncmoss-0.2.0 → syncmoss-0.2.2/syncmoss.egg-info}/PKG-INFO +1 -1
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss.egg-info/SOURCES.txt +3 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/syncmoss_test.py +0 -33
- syncmoss-0.2.2/tests/test_expression_validation.py +103 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/test_gui_smoke.py +30 -1
- syncmoss-0.2.2/tests/test_instrumental_metadata.py +649 -0
- syncmoss-0.2.0/syncmoss/minimi_lib.py +0 -607
- syncmoss-0.2.0/syncmoss/parameters/INSexp.txt +0 -1
- syncmoss-0.2.0/syncmoss/parameters/INSint.txt +0 -1
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_MacOS.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_PyPI.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_TagRelease.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_Tests.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_UploadRelease.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_Version.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_Wheels.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/_WindowsExe.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.github/workflows/release.yml +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/.vscode/settings.json +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/COPYING.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/LICENSE +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/Lib_test/test.mdl +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/NOTICE.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/README.md +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/requirements.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/setup.cfg +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Calibration.dat +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Hamiltonian_helper.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library/Hematite mineral test.mdl +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library/Magnetite mineral bulk test.mdl +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library/Wustite mineral test.mdl +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library/alfa iron test.mdl +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library_io.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/Library_window.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/constants.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/CheckBox.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/CheckBox_.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/CheckBox_L.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/CheckBox_L2.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/DU.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/Switch.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/UD.png +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/icons/icon_r.ico +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/models_positions.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/ABSorber3.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/Be.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/GCMS.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/INS_APS.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/KB.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/parameters/NFS.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/support_math.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/test_graf.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/test_param.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/theme_dark.json +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss/theme_light.json +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss.egg-info/dependency_links.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss.egg-info/entry_points.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss.egg-info/requires.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/syncmoss.egg-info/top_level.txt +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/conftest.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/test_library_io.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/test_model_io.py +0 -0
- {syncmoss-0.2.0 → syncmoss-0.2.2}/tests/test_support_math.py +0 -0
|
@@ -139,3 +139,13 @@ bundle/Windows/
|
|
|
139
139
|
syncmoss/*.mdl
|
|
140
140
|
syncmoss/*.png
|
|
141
141
|
syncmoss/*.svg
|
|
142
|
+
|
|
143
|
+
# Deprecated original minimiser, kept locally for reference / equivalence checks
|
|
144
|
+
# (the active implementation is now the optimized minimi_lib.py).
|
|
145
|
+
syncmoss/minimi_lib_old.py
|
|
146
|
+
|
|
147
|
+
# minimi_lib optimization: local-only test tooling and notes (depend on the
|
|
148
|
+
# git-ignored minimi_lib_old.py; kept out of the tracked repo).
|
|
149
|
+
verify_minimi.py
|
|
150
|
+
bench_coldstart.py
|
|
151
|
+
minimi_optimization_notes.txt
|
|
@@ -2,30 +2,49 @@
|
|
|
2
2
|
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
-
# Hidden imports
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
# ── Hidden imports ──────────────────────────────────────────────
|
|
6
|
+
# numba/llvmlite/scipy do a lot of dynamic importing, so collect their submodules
|
|
7
|
+
# (this keeps core modules like scipy._cyutility that the hooks miss). The big
|
|
8
|
+
# unused scipy subpackages are pruned again via scipy_excludes below; numba's own
|
|
9
|
+
# test-suite is filtered out (not needed at runtime, and it emits a wall of
|
|
10
|
+
# "hidden import not found" warnings for its CUDA tests).
|
|
11
|
+
hiddenimports = collect_submodules('numba', filter=lambda name: 'tests' not in name)
|
|
9
12
|
hiddenimports += collect_submodules('llvmlite')
|
|
13
|
+
hiddenimports += collect_submodules('scipy')
|
|
14
|
+
# Only the Qt modules SYNCmoss actually uses. We deliberately do NOT
|
|
15
|
+
# collect_submodules('PySide6'): that drags in the QtQuick / QtQml / Qt3D /
|
|
16
|
+
# WebEngine frameworks (100+ MB) the app never uses. The PySide6 PyInstaller
|
|
17
|
+
# hook bundles the Qt frameworks/plugins matching just these imports. This
|
|
18
|
+
# matters most on macOS, where the build is universal2 (two architectures) and
|
|
19
|
+
# UPX is not applied.
|
|
20
|
+
hiddenimports += ['PySide6.QtCore', 'PySide6.QtGui', 'PySide6.QtWidgets']
|
|
10
21
|
# GUI smoke driver lives in tests/syncmoss_test.py; bundling it lets the frozen
|
|
11
22
|
# binary self-test via `SYNCmoss --test` (see syncmoss/main.py).
|
|
12
23
|
hiddenimports += ['syncmoss_test']
|
|
13
24
|
|
|
14
|
-
|
|
25
|
+
# llvmlite carries native data/bitcode that must be collected.
|
|
15
26
|
llvmlite_data = collect_data_files('llvmlite')
|
|
27
|
+
# On macOS, missing platform input context plugins can lead to non-editable
|
|
28
|
+
# QLineEdit/QTable widgets in frozen apps. Bundle these explicitly.
|
|
29
|
+
qt_plugin_data = collect_data_files(
|
|
30
|
+
'PySide6',
|
|
31
|
+
includes=[
|
|
32
|
+
'Qt/plugins/platforms/*',
|
|
33
|
+
'Qt/plugins/platforminputcontexts/*',
|
|
34
|
+
'Qt/plugins/styles/*',
|
|
35
|
+
],
|
|
36
|
+
)
|
|
16
37
|
|
|
17
38
|
syncmoss_dir = os.path.abspath(os.path.join(SPECPATH, '..', 'syncmoss'))
|
|
18
39
|
project_dir = os.path.abspath(os.path.join(SPECPATH, '..'))
|
|
19
40
|
tests_dir = os.path.abspath(os.path.join(SPECPATH, '..', 'tests'))
|
|
20
41
|
|
|
21
|
-
|
|
42
|
+
# Application resources (icons/, parameters/, themes, etc.) are copied into the
|
|
43
|
+
# app bundle by macos_bundle.py after the build.
|
|
44
|
+
datas = llvmlite_data + qt_plugin_data
|
|
22
45
|
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# excludes win over collect_submodules above, so they are pruned from the bundle.
|
|
26
|
-
# This matters most on macOS, where the build is universal2 (two architectures)
|
|
27
|
-
# and UPX compression is not applied. The CI "--test" smoke run guards against
|
|
28
|
-
# accidentally excluding something the app actually needs.
|
|
46
|
+
# Unused-Qt Python module excludes (belt-and-suspenders, since PySide6 is no
|
|
47
|
+
# longer force-collected). The CI "--test" smoke run guards against over-excluding.
|
|
29
48
|
qt_excludes = [
|
|
30
49
|
'PySide6.QtWebEngineCore', 'PySide6.QtWebEngineWidgets', 'PySide6.QtWebEngineQuick',
|
|
31
50
|
'PySide6.QtWebChannel', 'PySide6.QtWebSockets', 'PySide6.QtWebView',
|
|
@@ -42,6 +61,15 @@ qt_excludes = [
|
|
|
42
61
|
'PySide6.QtUiTools', 'PySide6.QtNetworkAuth', 'PySide6.QtRemoteObjects',
|
|
43
62
|
'PySide6.QtTextToSpeech', 'PySide6.QtScxml', 'PySide6.QtStateMachine',
|
|
44
63
|
]
|
|
64
|
+
# scipy is only used for scipy.linalg.eig; exclude the big unused subpackages.
|
|
65
|
+
# (linalg, _lib, special and sparse are kept: scipy.linalg's import chain may
|
|
66
|
+
# touch special/sparse.)
|
|
67
|
+
scipy_excludes = [
|
|
68
|
+
'scipy.stats', 'scipy.optimize', 'scipy.spatial', 'scipy.interpolate',
|
|
69
|
+
'scipy.integrate', 'scipy.fft', 'scipy.fftpack', 'scipy.ndimage',
|
|
70
|
+
'scipy.signal', 'scipy.io', 'scipy.cluster', 'scipy.odr',
|
|
71
|
+
'scipy.datasets', 'scipy.misc',
|
|
72
|
+
]
|
|
45
73
|
# pytest is only used by the source test-suite; it must not be pulled into the
|
|
46
74
|
# frozen binary (the smoke driver imports it lazily / guarded).
|
|
47
75
|
other_excludes = ['tkinter', 'PyQt5', 'PyQt6', 'PySide2', 'pytest', '_pytest', 'pluggy']
|
|
@@ -53,7 +81,9 @@ a = Analysis(
|
|
|
53
81
|
pathex=[project_dir, tests_dir],
|
|
54
82
|
hooksconfig={
|
|
55
83
|
'matplotlib': {
|
|
56
|
-
|
|
84
|
+
# Only the backends SYNCmoss uses (Qt for the GUI, Agg/SVG/PDF for
|
|
85
|
+
# saving). 'all' pulls every backend incl. the tk one.
|
|
86
|
+
'backends': ['Agg', 'QtAgg', 'SVG', 'PDF'],
|
|
57
87
|
},
|
|
58
88
|
},
|
|
59
89
|
binaries=[],
|
|
@@ -61,7 +91,7 @@ a = Analysis(
|
|
|
61
91
|
hiddenimports=hiddenimports,
|
|
62
92
|
hookspath=[],
|
|
63
93
|
runtime_hooks=[],
|
|
64
|
-
excludes=qt_excludes + other_excludes,
|
|
94
|
+
excludes=qt_excludes + scipy_excludes + other_excludes,
|
|
65
95
|
cipher=block_cipher,
|
|
66
96
|
noarchive=False,
|
|
67
97
|
)
|
|
@@ -76,9 +106,9 @@ exe_gui = EXE(
|
|
|
76
106
|
name='SYNCmoss',
|
|
77
107
|
debug=False,
|
|
78
108
|
bootloader_ignore_signals=False,
|
|
79
|
-
strip=
|
|
109
|
+
strip=False,
|
|
80
110
|
upx=False,
|
|
81
|
-
console=
|
|
111
|
+
console=False,
|
|
82
112
|
)
|
|
83
113
|
|
|
84
114
|
coll = COLLECT(
|
|
@@ -86,7 +116,7 @@ coll = COLLECT(
|
|
|
86
116
|
a.binaries,
|
|
87
117
|
a.zipfiles,
|
|
88
118
|
a.datas,
|
|
89
|
-
strip=
|
|
119
|
+
strip=False,
|
|
90
120
|
upx=False,
|
|
91
121
|
upx_exclude=[],
|
|
92
122
|
name='SYNCmoss',
|
|
@@ -3,26 +3,35 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
5
|
# ── Hidden imports ──────────────────────────────────────────────
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
#
|
|
9
|
-
|
|
6
|
+
# numba/llvmlite/scipy do a lot of dynamic importing, so collect their submodules
|
|
7
|
+
# (this keeps core modules like scipy._cyutility that the hooks miss). The big
|
|
8
|
+
# unused scipy subpackages are pruned again via scipy_excludes below; numba's own
|
|
9
|
+
# test-suite is filtered out (not needed at runtime, and it emits a wall of
|
|
10
|
+
# "hidden import not found" warnings for its CUDA tests).
|
|
11
|
+
hiddenimports = collect_submodules('numba', filter=lambda name: 'tests' not in name)
|
|
10
12
|
hiddenimports += collect_submodules('llvmlite')
|
|
13
|
+
hiddenimports += collect_submodules('scipy')
|
|
14
|
+
# Only the Qt modules SYNCmoss actually uses. We deliberately do NOT
|
|
15
|
+
# collect_submodules('PySide6'): that drags in the QtQuick / QtQml / Qt3D /
|
|
16
|
+
# WebEngine frameworks (100+ MB) the app never uses. The PySide6 PyInstaller
|
|
17
|
+
# hook bundles the Qt frameworks/plugins matching just these imports.
|
|
18
|
+
hiddenimports += ['PySide6.QtCore', 'PySide6.QtGui', 'PySide6.QtWidgets']
|
|
11
19
|
# GUI smoke driver lives in tests/syncmoss_test.py; bundling it lets the frozen
|
|
12
20
|
# binary self-test via `SYNCmoss --test` (see syncmoss/main.py).
|
|
13
21
|
hiddenimports += ['syncmoss_test']
|
|
14
|
-
|
|
15
|
-
#
|
|
22
|
+
|
|
23
|
+
# scipy is only needed for scipy.linalg.eig (the import-time "dummy" in
|
|
24
|
+
# models.py); scipy_excludes below drops the big unused subpackages.
|
|
25
|
+
# llvmlite carries native data/bitcode that must be collected.
|
|
16
26
|
llvmlite_data = collect_data_files('llvmlite')
|
|
17
27
|
|
|
18
28
|
syncmoss_dir = os.path.abspath(os.path.join(SPECPATH, '..', 'syncmoss'))
|
|
19
29
|
project_dir = os.path.abspath(os.path.join(SPECPATH, '..'))
|
|
20
30
|
tests_dir = os.path.abspath(os.path.join(SPECPATH, '..', 'tests'))
|
|
21
31
|
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
datas = scipy_data + llvmlite_data
|
|
32
|
+
# Application resources (icons/, parameters/, themes, etc.) are copied next to
|
|
33
|
+
# the .exe by bundle.py after the build.
|
|
34
|
+
datas = llvmlite_data
|
|
26
35
|
|
|
27
36
|
# SYNCmoss only uses PySide6.QtWidgets / QtGui / QtCore. Excluding the large,
|
|
28
37
|
# unused Qt frameworks keeps the bundle small; excludes win over the
|
|
@@ -44,6 +53,15 @@ qt_excludes = [
|
|
|
44
53
|
'PySide6.QtUiTools', 'PySide6.QtNetworkAuth', 'PySide6.QtRemoteObjects',
|
|
45
54
|
'PySide6.QtTextToSpeech', 'PySide6.QtScxml', 'PySide6.QtStateMachine',
|
|
46
55
|
]
|
|
56
|
+
# scipy is only used for scipy.linalg.eig; exclude the big unused subpackages.
|
|
57
|
+
# (linalg, _lib, special and sparse are kept: scipy.linalg's import chain may
|
|
58
|
+
# touch special/sparse.)
|
|
59
|
+
scipy_excludes = [
|
|
60
|
+
'scipy.stats', 'scipy.optimize', 'scipy.spatial', 'scipy.interpolate',
|
|
61
|
+
'scipy.integrate', 'scipy.fft', 'scipy.fftpack', 'scipy.ndimage',
|
|
62
|
+
'scipy.signal', 'scipy.io', 'scipy.cluster', 'scipy.odr',
|
|
63
|
+
'scipy.datasets', 'scipy.misc',
|
|
64
|
+
]
|
|
47
65
|
# pytest is only used by the source test-suite; it must not be pulled into the
|
|
48
66
|
# frozen binary (the smoke driver imports it lazily / guarded).
|
|
49
67
|
other_excludes = ['tkinter', 'PyQt5', 'PyQt6', 'PySide2', 'pytest', '_pytest', 'pluggy']
|
|
@@ -55,7 +73,9 @@ a = Analysis(
|
|
|
55
73
|
pathex=[project_dir, tests_dir],
|
|
56
74
|
hooksconfig={
|
|
57
75
|
'matplotlib': {
|
|
58
|
-
|
|
76
|
+
# Only the backends SYNCmoss uses (Qt for the GUI, Agg/SVG/PDF for
|
|
77
|
+
# saving). 'all' pulls every backend incl. the tk one.
|
|
78
|
+
'backends': ['Agg', 'QtAgg', 'SVG', 'PDF'],
|
|
59
79
|
},
|
|
60
80
|
},
|
|
61
81
|
binaries=[],
|
|
@@ -63,7 +83,7 @@ a = Analysis(
|
|
|
63
83
|
hiddenimports=hiddenimports,
|
|
64
84
|
hookspath=[],
|
|
65
85
|
runtime_hooks=[],
|
|
66
|
-
excludes=qt_excludes + other_excludes,
|
|
86
|
+
excludes=qt_excludes + scipy_excludes + other_excludes,
|
|
67
87
|
cipher=block_cipher,
|
|
68
88
|
noarchive=False,
|
|
69
89
|
)
|
|
@@ -66,7 +66,6 @@ def copy_resources():
|
|
|
66
66
|
# from syncmoss/
|
|
67
67
|
os.path.join(syncmoss_DIR, "theme_dark.json"),
|
|
68
68
|
os.path.join(syncmoss_DIR, "theme_light.json"),
|
|
69
|
-
os.path.join(syncmoss_DIR, "Calibration.dat"),
|
|
70
69
|
# from repo root
|
|
71
70
|
os.path.join(ROOT_DIR, "COPYING.txt"),
|
|
72
71
|
os.path.join(ROOT_DIR, "NOTICE.txt"),
|
|
@@ -117,7 +116,7 @@ def verify():
|
|
|
117
116
|
"parameters/INSint.txt",
|
|
118
117
|
"theme_dark.json",
|
|
119
118
|
"theme_light.json",
|
|
120
|
-
"Calibration.dat",
|
|
119
|
+
"parameters/Calibration.dat",
|
|
121
120
|
"COPYING.txt",
|
|
122
121
|
"NOTICE.txt",
|
|
123
122
|
"LICENSE",
|
|
@@ -69,7 +69,6 @@ def copy_resources(app_dir: str):
|
|
|
69
69
|
root_files = [
|
|
70
70
|
os.path.join(SYNCMOSS_DIR, "theme_dark.json"),
|
|
71
71
|
os.path.join(SYNCMOSS_DIR, "theme_light.json"),
|
|
72
|
-
os.path.join(SYNCMOSS_DIR, "Calibration.dat"),
|
|
73
72
|
os.path.join(ROOT_DIR, "COPYING.txt"),
|
|
74
73
|
os.path.join(ROOT_DIR, "NOTICE.txt"),
|
|
75
74
|
os.path.join(ROOT_DIR, "LICENSE"),
|
|
@@ -113,6 +112,7 @@ def verify(app_dir: str):
|
|
|
113
112
|
"Contents/MacOS/icons/CheckBox.png",
|
|
114
113
|
"Contents/MacOS/parameters/Be.txt",
|
|
115
114
|
"Contents/MacOS/parameters/KB.txt",
|
|
115
|
+
"Contents/MacOS/parameters/Calibration.dat",
|
|
116
116
|
"Contents/MacOS/theme_dark.json",
|
|
117
117
|
"Contents/MacOS/theme_light.json",
|
|
118
118
|
]
|
|
@@ -31,6 +31,7 @@ import syncmoss.minimi_lib as mi
|
|
|
31
31
|
import syncmoss.models as m5
|
|
32
32
|
import multiprocessing as mp
|
|
33
33
|
import matplotlib.pyplot as plt
|
|
34
|
+
import os
|
|
34
35
|
import platform
|
|
35
36
|
import re
|
|
36
37
|
import time
|
|
@@ -541,7 +542,7 @@ def Calibration(dir_path, Cal_file, pool, VVV, INS, JN, x0, MulCo, Vel_start = 1
|
|
|
541
542
|
model = ['Sextet', 'Sextet', 'Doublet']
|
|
542
543
|
|
|
543
544
|
try:
|
|
544
|
-
Be_param = np.genfromtxt(
|
|
545
|
+
Be_param = np.genfromtxt(os.path.join(dir_path, 'Be.txt'), delimiter='\t', skip_footer=0)
|
|
545
546
|
print('file was read')
|
|
546
547
|
except:
|
|
547
548
|
Be_param = np.array([0.057, 0.066, -0.261, 0.098, 0.375, 0.772, 1])
|
|
@@ -742,7 +743,7 @@ def Calibration(dir_path, Cal_file, pool, VVV, INS, JN, x0, MulCo, Vel_start = 1
|
|
|
742
743
|
|
|
743
744
|
ax.text(len(xn2)/2, max(id[0]) + 10 * np.sqrt(max(id[0])), str('lin ')*(method==1) + str('sin ')*(method==0) + str(n1) + str(' ') + str(n2), color='r', fontsize=8, horizontalalignment='center')
|
|
744
745
|
|
|
745
|
-
fig.savefig('calibr.png', bbox_inches='tight')
|
|
746
|
+
fig.savefig(os.path.join(dir_path, 'calibr.png'), bbox_inches='tight')
|
|
746
747
|
plt.close()
|
|
747
748
|
|
|
748
749
|
print('Shift due to instrumental function ', INS_shift)
|
|
@@ -14,7 +14,11 @@ from syncmoss.constants import number_of_baseline_parameters, numco
|
|
|
14
14
|
from syncmoss.model_io import mod_len_def as mod_len_def_full, read_model as read_model_full
|
|
15
15
|
from syncmoss.models_positions import mod_pos
|
|
16
16
|
from syncmoss.spectrum_io import load_spectrum
|
|
17
|
-
from syncmoss.instrumental_io import
|
|
17
|
+
from syncmoss.instrumental_io import (
|
|
18
|
+
resolve_instrumental_for_file,
|
|
19
|
+
compute_norm,
|
|
20
|
+
same_method_params,
|
|
21
|
+
)
|
|
18
22
|
|
|
19
23
|
|
|
20
24
|
|
|
@@ -296,62 +300,95 @@ def fit_single_spectrum(app, spectrum_file, pool, background=None, sequence_para
|
|
|
296
300
|
print(f"[Fitting] X range: {A[0]:.2f} to {A[-1]:.2f}")
|
|
297
301
|
print(f"[Fitting] Y range: {B.min():.2f} to {B.max():.2f}")
|
|
298
302
|
|
|
299
|
-
#
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
experimental_method = 1
|
|
303
|
-
elif app.SMS_fit.isChecked():
|
|
304
|
-
VVV = 3 # SMS method
|
|
305
|
-
experimental_method = 3
|
|
306
|
-
else:
|
|
303
|
+
# A method checkbox must be selected (it is the fallback when a spectrum
|
|
304
|
+
# carries no .dat instrumental metadata)
|
|
305
|
+
if not app.MS_fit.isChecked() and not app.SMS_fit.isChecked():
|
|
307
306
|
return {
|
|
308
307
|
'success': False,
|
|
309
308
|
'message': 'No fitting method selected (MS or SMS)'
|
|
310
309
|
}
|
|
311
|
-
|
|
312
|
-
#
|
|
310
|
+
|
|
311
|
+
# Resolve instrumental parameters per spectrum. Each spectrum may be CMS
|
|
312
|
+
# or SMS depending on its own .dat metadata (#@GCMS vs #@INSexp/#@INSint)
|
|
313
|
+
# when the "use instrumental function from .dat file" option is enabled;
|
|
314
|
+
# otherwise the UI-selected method with the internal values is used.
|
|
313
315
|
JN = int(app.JN0)
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
use_dat_metadata = bool(getattr(app, 'use_dat_instrumental_metadata', True))
|
|
317
|
+
files_for_ins = list(spectrum_files) if is_simultaneous else [spectrum_file]
|
|
318
|
+
|
|
319
|
+
method_params_list = []
|
|
320
|
+
for ins_file in files_for_ins:
|
|
321
|
+
mp_i = resolve_instrumental_for_file(app, ins_file, use_dat_metadata=use_dat_metadata)
|
|
322
|
+
mp_i['Norm'] = compute_norm(pool, JN, mp_i)
|
|
323
|
+
print('Normalization integral equal to', mp_i['Norm'])
|
|
324
|
+
method_params_list.append(mp_i)
|
|
325
|
+
mp0 = method_params_list[0]
|
|
326
|
+
|
|
327
|
+
note_lines = [mp_i['note'] for mp_i in method_params_list]
|
|
328
|
+
if len({mp_i['method'] for mp_i in method_params_list}) > 1:
|
|
329
|
+
note_lines.insert(0, "Mixed-method simultaneous fit: CMS and SMS spectra are fitted together.")
|
|
330
|
+
instrumental_note = '\n'.join(note_lines)
|
|
331
|
+
print(f"[Fitting] {instrumental_note}")
|
|
332
|
+
|
|
333
|
+
if is_simultaneous:
|
|
334
|
+
# Split model at Nbaseline boundaries
|
|
335
|
+
model_separate = []
|
|
336
|
+
startM = 0
|
|
337
|
+
for i in range(len(model)):
|
|
338
|
+
if model[i] == 'Nbaseline':
|
|
339
|
+
model_separate.append(model[startM:i])
|
|
340
|
+
startM = i + 1
|
|
341
|
+
model_separate.append(model[startM:])
|
|
342
|
+
|
|
343
|
+
# Calculate parameter indices for each spectrum
|
|
344
|
+
begining_spc = [0]
|
|
345
|
+
start_cont_par = number_of_baseline_parameters
|
|
346
|
+
param_names_full = app.params_table.get_parameter_names()
|
|
347
|
+
for i in range(1, len(param_names_full)):
|
|
348
|
+
param_names = param_names_full[i]
|
|
349
|
+
if len(param_names) > 0 and param_names[0] == 'Ns': # Start of new spectrum
|
|
350
|
+
begining_spc.append(start_cont_par)
|
|
351
|
+
for j in range(len(param_names)):
|
|
352
|
+
if param_names[j] != '':
|
|
353
|
+
start_cont_par += 1
|
|
354
|
+
|
|
355
|
+
print(f"[Fitting] Simultaneous - model_separate: {model_separate}")
|
|
356
|
+
print(f"[Fitting] Simultaneous - begining_spc: {begining_spc}")
|
|
357
|
+
|
|
358
|
+
# Per-section slices of the Distri/Cor expression lists (used after the
|
|
359
|
+
# fit to rebuild each section's sub-spectra for plotting)
|
|
360
|
+
distr_bounds = np.cumsum([0] + [ms.count('Distr') for ms in model_separate])
|
|
361
|
+
corr_bounds = np.cumsum([0] + [ms.count('Corr') for ms in model_separate])
|
|
362
|
+
|
|
363
|
+
def section_parameters(p_full, idx):
|
|
364
|
+
if idx < len(begining_spc) - 1:
|
|
365
|
+
return p_full[begining_spc[idx]:begining_spc[idx + 1]]
|
|
366
|
+
return p_full[begining_spc[idx]:]
|
|
367
|
+
|
|
368
|
+
uniform_method = all(same_method_params(mp0, mp_i) for mp_i in method_params_list[1:])
|
|
369
|
+
|
|
370
|
+
if not is_simultaneous or uniform_method:
|
|
371
|
+
# Uniform instrumental settings: a single TI call over the whole model
|
|
372
|
+
# (TI splits Nbaseline sections internally) — the original code path.
|
|
327
373
|
def func(x, p):
|
|
328
|
-
return m5.TI(x, p, model, JN, pool,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
#
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
use_dat_metadata=use_dat_metadata,
|
|
343
|
-
)
|
|
344
|
-
print(f"[Fitting] {instrumental_note}")
|
|
345
|
-
|
|
346
|
-
# Calculate normalization integral
|
|
347
|
-
pNorm = np.array([float(0)] * number_of_baseline_parameters)
|
|
348
|
-
pNorm[0] = 1
|
|
349
|
-
Norm = m5.TI(np.array([float(1000)]), pNorm, [], JN, pool, x0_val, MulCo_val, INS, [0], [0])[0]
|
|
350
|
-
print('Normalization integral equal to', Norm)
|
|
351
|
-
|
|
374
|
+
return m5.TI(x, p, model, JN, pool, mp0['x0'], mp0['MulCo'], mp0['INS'],
|
|
375
|
+
Distri, Cor, Met=mp0['Met'], Norm=mp0['Norm'])
|
|
376
|
+
else:
|
|
377
|
+
# Dedicated per-section instrumental parameters (e.g. mixing CMS and
|
|
378
|
+
# SMS): TI receives one value per section as lists. The full model and
|
|
379
|
+
# full p are still passed, so cross-spectrum links and Distr/Cor p[i]
|
|
380
|
+
# references resolve exactly as in the uniform path — no per-section
|
|
381
|
+
# bookkeeping leaks into this module.
|
|
382
|
+
x0_list = [mp_i['x0'] for mp_i in method_params_list]
|
|
383
|
+
mulco_list = [mp_i['MulCo'] for mp_i in method_params_list]
|
|
384
|
+
ins_list = [mp_i['INS'] for mp_i in method_params_list]
|
|
385
|
+
met_list = [mp_i['Met'] for mp_i in method_params_list]
|
|
386
|
+
norm_list = [mp_i['Norm'] for mp_i in method_params_list]
|
|
387
|
+
|
|
352
388
|
def func(x, p):
|
|
353
|
-
return m5.TI(x, p, model, JN, pool,
|
|
354
|
-
|
|
389
|
+
return m5.TI(x, p, model, JN, pool, x0_list, mulco_list, ins_list,
|
|
390
|
+
Distri, Cor, Met=met_list, Norm=norm_list)
|
|
391
|
+
|
|
355
392
|
# Set up bounds and fixed parameters
|
|
356
393
|
# For now, use unbounded optimization
|
|
357
394
|
bounds = np.array([[-np.inf] * len(p), [np.inf] * len(p)], dtype=float)
|
|
@@ -480,56 +517,28 @@ def fit_single_spectrum(app, spectrum_file, pool, background=None, sequence_para
|
|
|
480
517
|
SPC_f = func(A, p)
|
|
481
518
|
|
|
482
519
|
# For simultaneous fitting, we need to separate results for each spectrum
|
|
520
|
+
# (model_separate and begining_spc were computed before the minimization)
|
|
483
521
|
if is_simultaneous:
|
|
484
|
-
# Separate model and parameters for each spectrum
|
|
485
|
-
model_separate = []
|
|
486
|
-
startM = 0
|
|
487
|
-
for i in range(len(model)):
|
|
488
|
-
if model[i] == 'Nbaseline':
|
|
489
|
-
model_separate.append(model[startM:i])
|
|
490
|
-
startM = i + 1
|
|
491
|
-
model_separate.append(model[startM:])
|
|
492
|
-
|
|
493
|
-
# Calculate parameter indices for each spectrum
|
|
494
|
-
begining_spc = [0]
|
|
495
|
-
start_cont_par = number_of_baseline_parameters
|
|
496
|
-
param_names_full = app.params_table.get_parameter_names()
|
|
497
|
-
for i in range(1, len(param_names_full)):
|
|
498
|
-
param_names = param_names_full[i]
|
|
499
|
-
if len(param_names) > 0 and param_names[0] == 'Ns': # Start of new spectrum
|
|
500
|
-
begining_spc.append(start_cont_par)
|
|
501
|
-
for j in range(len(param_names)):
|
|
502
|
-
if param_names[j] != '':
|
|
503
|
-
start_cont_par += 1
|
|
504
|
-
|
|
505
|
-
print(f"[Fitting] Simultaneous - model_separate: {model_separate}")
|
|
506
|
-
print(f"[Fitting] Simultaneous - begining_spc: {begining_spc}")
|
|
507
|
-
|
|
508
522
|
# Substitute Distri and Cor parameter values ONCE using full model and full p
|
|
509
523
|
# This ensures constrained parameters are correctly substituted
|
|
510
524
|
Distri_substituted = np.copy(Distri)
|
|
511
525
|
Cor_substituted = np.copy(Cor)
|
|
512
526
|
if len(Distri) > 0 or len(Cor) > 0:
|
|
513
527
|
_, _, Distri_substituted, Cor_substituted, _, _ = create_subspectra(app, model, Distri, Cor, p)
|
|
514
|
-
|
|
515
|
-
# Calculate fitted spectrum for each section separately
|
|
516
|
-
#
|
|
528
|
+
|
|
529
|
+
# Calculate fitted spectrum for each section separately, each with the
|
|
530
|
+
# instrumental parameters resolved for that section's spectrum
|
|
517
531
|
SPC_f_list = []
|
|
518
532
|
for NumSpc in range(number_of_spectra):
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if VVV == 1: # MS method
|
|
529
|
-
SPC_f_separate = m5.TI(A_list[NumSpc], p_separate, model_for_spectrum, JN, pool, 0.0, MulCo_val, INS, Distri_substituted, Cor_substituted, Met=1, Norm=Norm)
|
|
530
|
-
elif VVV == 3: # SMS method
|
|
531
|
-
SPC_f_separate = m5.TI(A_list[NumSpc], p_separate, model_for_spectrum, JN, pool, x0_val, MulCo_val, INS, Distri_substituted, Cor_substituted, Norm=Norm)
|
|
532
|
-
|
|
533
|
+
p_separate = section_parameters(p, NumSpc)
|
|
534
|
+
mp_i = method_params_list[NumSpc]
|
|
535
|
+
d_slice = list(Distri_substituted[distr_bounds[NumSpc]:distr_bounds[NumSpc + 1]])
|
|
536
|
+
c_slice = list(Cor_substituted[corr_bounds[NumSpc]:corr_bounds[NumSpc + 1]])
|
|
537
|
+
SPC_f_separate = m5.TI(A_list[NumSpc], p_separate, model_separate[NumSpc], JN, pool,
|
|
538
|
+
mp_i['x0'], mp_i['MulCo'], mp_i['INS'],
|
|
539
|
+
d_slice if len(d_slice) > 0 else [0],
|
|
540
|
+
c_slice if len(c_slice) > 0 else [0],
|
|
541
|
+
Met=mp_i['Met'], Norm=mp_i['Norm'])
|
|
533
542
|
SPC_f_list.append(SPC_f_separate)
|
|
534
543
|
|
|
535
544
|
# Now calculate subspectra for plotting
|
|
@@ -547,15 +556,12 @@ def fit_single_spectrum(app, spectrum_file, pool, background=None, sequence_para
|
|
|
547
556
|
Cor_work = np.copy(Cor)
|
|
548
557
|
|
|
549
558
|
for NumSpc in range(number_of_spectra):
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
else:
|
|
554
|
-
p_separate = p[begining_spc[NumSpc]:]
|
|
555
|
-
|
|
559
|
+
p_separate = section_parameters(p, NumSpc)
|
|
560
|
+
mp_i = method_params_list[NumSpc]
|
|
561
|
+
|
|
556
562
|
# Calculate subspectra
|
|
557
563
|
Ps, Psm, Distri_t, Cor_t, Di, Co = create_subspectra(app, model_separate[NumSpc], Distri_work, Cor_work, p_separate)
|
|
558
|
-
|
|
564
|
+
|
|
559
565
|
FS = []
|
|
560
566
|
FS_pos = []
|
|
561
567
|
for i in range(len(Ps)):
|
|
@@ -563,16 +569,13 @@ def fit_single_spectrum(app, spectrum_file, pool, background=None, sequence_para
|
|
|
563
569
|
DiSt = sum([Psm[j].count('Distr') for j in range(i)])
|
|
564
570
|
CoEn = CoSt + Psm[i].count('Corr')
|
|
565
571
|
DiEn = DiSt + Psm[i].count('Distr')
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
Distri_t[DiSt:DiEn], Cor_t[CoSt:CoEn], Norm=Norm)
|
|
574
|
-
positions = mod_pos(Ps[i], Psm[i], INS)
|
|
575
|
-
|
|
572
|
+
|
|
573
|
+
subspectrum = m5.TI(A_list[NumSpc], Ps[i], Psm[i], JN, pool,
|
|
574
|
+
mp_i['x0'], mp_i['MulCo'], mp_i['INS'],
|
|
575
|
+
Distri_t[DiSt:DiEn], Cor_t[CoSt:CoEn],
|
|
576
|
+
Met=mp_i['Met'], Norm=mp_i['Norm'])
|
|
577
|
+
positions = mod_pos(Ps[i], Psm[i], mp_i['INS'], Met=mp_i['Met'])
|
|
578
|
+
|
|
576
579
|
FS.append(subspectrum)
|
|
577
580
|
FS_pos.append(positions)
|
|
578
581
|
|
|
@@ -623,16 +626,12 @@ def fit_single_spectrum(app, spectrum_file, pool, background=None, sequence_para
|
|
|
623
626
|
CoEn = CoSt + Psm[i].count('Corr')
|
|
624
627
|
DiEn = DiSt + Psm[i].count('Distr')
|
|
625
628
|
print(Ps[i], Psm[i])
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
subspectrum = m5.TI(A, Ps[i], Psm[i], JN, pool, x0_val, MulCo_val, INS,
|
|
633
|
-
Distri_t[DiSt:DiEn], Cor_t[CoSt:CoEn], Norm=Norm)
|
|
634
|
-
# Calculate positions for this subspectrum
|
|
635
|
-
positions = mod_pos(Ps[i], Psm[i], INS)
|
|
629
|
+
subspectrum = m5.TI(A, Ps[i], Psm[i], JN, pool,
|
|
630
|
+
mp0['x0'], mp0['MulCo'], mp0['INS'],
|
|
631
|
+
Distri_t[DiSt:DiEn], Cor_t[CoSt:CoEn],
|
|
632
|
+
Met=mp0['Met'], Norm=mp0['Norm'])
|
|
633
|
+
# Calculate positions for this subspectrum
|
|
634
|
+
positions = mod_pos(Ps[i], Psm[i], mp0['INS'], Met=mp0['Met'])
|
|
636
635
|
FS.append(subspectrum)
|
|
637
636
|
FS_pos.append(positions)
|
|
638
637
|
|