opennmr 0.1.0__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.
- opennmr-0.1.0/LICENSE +21 -0
- opennmr-0.1.0/PKG-INFO +449 -0
- opennmr-0.1.0/README.md +418 -0
- opennmr-0.1.0/pyproject.toml +60 -0
- opennmr-0.1.0/setup.cfg +4 -0
- opennmr-0.1.0/src/opennmr/__init__.py +25 -0
- opennmr-0.1.0/src/opennmr/_version.py +22 -0
- opennmr-0.1.0/src/opennmr/core/__init__.py +4 -0
- opennmr-0.1.0/src/opennmr/core/acquisition.py +120 -0
- opennmr-0.1.0/src/opennmr/core/fit_progress.py +504 -0
- opennmr-0.1.0/src/opennmr/core/registry.py +300 -0
- opennmr-0.1.0/src/opennmr/core/spectrum.py +172 -0
- opennmr-0.1.0/src/opennmr/gui/__init__.py +0 -0
- opennmr-0.1.0/src/opennmr/gui/app.py +50 -0
- opennmr-0.1.0/src/opennmr/gui/arithmetic_panel.py +320 -0
- opennmr-0.1.0/src/opennmr/gui/batch_relaxation_panel.py +313 -0
- opennmr-0.1.0/src/opennmr/gui/combined_panels.py +2671 -0
- opennmr-0.1.0/src/opennmr/gui/combined_section.py +69 -0
- opennmr-0.1.0/src/opennmr/gui/compare_2d.py +320 -0
- opennmr-0.1.0/src/opennmr/gui/cp_buildup_panel.py +810 -0
- opennmr-0.1.0/src/opennmr/gui/cpmg_panel.py +367 -0
- opennmr-0.1.0/src/opennmr/gui/curve_fit_plot_box.py +161 -0
- opennmr-0.1.0/src/opennmr/gui/czjzek_panels.py +1009 -0
- opennmr-0.1.0/src/opennmr/gui/data_directory.py +56 -0
- opennmr-0.1.0/src/opennmr/gui/dft_panel.py +1022 -0
- opennmr-0.1.0/src/opennmr/gui/dnp_panel.py +1080 -0
- opennmr-0.1.0/src/opennmr/gui/drift_panel.py +443 -0
- opennmr-0.1.0/src/opennmr/gui/dynamics_panels.py +1520 -0
- opennmr-0.1.0/src/opennmr/gui/fact_box.py +124 -0
- opennmr-0.1.0/src/opennmr/gui/fit_display_panel.py +268 -0
- opennmr-0.1.0/src/opennmr/gui/fit_models_panel.py +388 -0
- opennmr-0.1.0/src/opennmr/gui/fit_progress_dialog.py +126 -0
- opennmr-0.1.0/src/opennmr/gui/fit_view_dialog.py +107 -0
- opennmr-0.1.0/src/opennmr/gui/inset_dialog.py +263 -0
- opennmr-0.1.0/src/opennmr/gui/jcoupling_panels.py +2244 -0
- opennmr-0.1.0/src/opennmr/gui/loader.py +146 -0
- opennmr-0.1.0/src/opennmr/gui/main_window.py +29527 -0
- opennmr-0.1.0/src/opennmr/gui/manual_baseline_box.py +434 -0
- opennmr-0.1.0/src/opennmr/gui/multi_fit_panel.py +1516 -0
- opennmr-0.1.0/src/opennmr/gui/offset_tracker_panel.py +841 -0
- opennmr-0.1.0/src/opennmr/gui/panel_state.py +314 -0
- opennmr-0.1.0/src/opennmr/gui/periodic_table_panel.py +946 -0
- opennmr-0.1.0/src/opennmr/gui/plot_options_dialog.py +36 -0
- opennmr-0.1.0/src/opennmr/gui/pnmr_export_row.py +67 -0
- opennmr-0.1.0/src/opennmr/gui/pnmr_fit_panel.py +1178 -0
- opennmr-0.1.0/src/opennmr/gui/pnmr_panel.py +1559 -0
- opennmr-0.1.0/src/opennmr/gui/procno_dialog.py +105 -0
- opennmr-0.1.0/src/opennmr/gui/progress.py +253 -0
- opennmr-0.1.0/src/opennmr/gui/quadrupolar_panels.py +839 -0
- opennmr-0.1.0/src/opennmr/gui/regularisation_panel.py +630 -0
- opennmr-0.1.0/src/opennmr/gui/report_format.py +314 -0
- opennmr-0.1.0/src/opennmr/gui/resources/facts.dat +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_advanced_processing.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_applications.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_comparison.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_fitting.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_integrate.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_load_save_compare.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_periodictable.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_processing.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_relaxation.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_settings.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/launcher_simulations.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/logo.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/compare.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/curve_fitting.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/load.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/processing.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/settings.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/simulate.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/resources/toolbar/spectrum_fitting.png +0 -0
- opennmr-0.1.0/src/opennmr/gui/session.py +356 -0
- opennmr-0.1.0/src/opennmr/gui/shear_panel.py +542 -0
- opennmr-0.1.0/src/opennmr/gui/snr_panel.py +1106 -0
- opennmr-0.1.0/src/opennmr/gui/state_dialog.py +184 -0
- opennmr-0.1.0/src/opennmr/gui/stored_integrals_dialog.py +116 -0
- opennmr-0.1.0/src/opennmr/gui/style.py +348 -0
- opennmr-0.1.0/src/opennmr/gui/tensor_style_dialog.py +227 -0
- opennmr-0.1.0/src/opennmr/gui/tensor_view_panel.py +117 -0
- opennmr-0.1.0/src/opennmr/gui/welcome_wizard.py +285 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/__init__.py +0 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/advice_box.py +110 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/canvas.py +9010 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/collapsible_section.py +239 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/data_browser.py +374 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/drag_control.py +123 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/field_input.py +234 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/mode_button.py +75 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/notes_editor.py +160 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/ranges_edit.py +43 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/reflow.py +460 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/relative_spinbox.py +57 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/report_label.py +75 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/side_drawer.py +138 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/sideband_hint.py +32 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/sim_sites.py +317 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/simulation_canvas.py +64 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/typed_spinbox.py +182 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/wheel_guard.py +79 -0
- opennmr-0.1.0/src/opennmr/gui/widgets/wrapping_tabs.py +360 -0
- opennmr-0.1.0/src/opennmr/io/__init__.py +20 -0
- opennmr-0.1.0/src/opennmr/io/base.py +122 -0
- opennmr-0.1.0/src/opennmr/io/bruker.py +2444 -0
- opennmr-0.1.0/src/opennmr/io/bruker_pfg.py +583 -0
- opennmr-0.1.0/src/opennmr/io/cloud.py +133 -0
- opennmr-0.1.0/src/opennmr/io/digital_filter.py +173 -0
- opennmr-0.1.0/src/opennmr/io/history.py +1267 -0
- opennmr-0.1.0/src/opennmr/io/jcampdx.py +154 -0
- opennmr-0.1.0/src/opennmr/io/jeol.py +567 -0
- opennmr-0.1.0/src/opennmr/io/lists.py +288 -0
- opennmr-0.1.0/src/opennmr/io/persistence.py +330 -0
- opennmr-0.1.0/src/opennmr/io/serialize.py +102 -0
- opennmr-0.1.0/src/opennmr/io/spinsolve.py +1250 -0
- opennmr-0.1.0/src/opennmr/io/state.py +263 -0
- opennmr-0.1.0/src/opennmr/io/varian.py +405 -0
- opennmr-0.1.0/src/opennmr/io/varian_binary.py +156 -0
- opennmr-0.1.0/src/opennmr/io/xpulse.py +1625 -0
- opennmr-0.1.0/src/opennmr/notebook/__init__.py +522 -0
- opennmr-0.1.0/src/opennmr/notebook/arithmetic.py +198 -0
- opennmr-0.1.0/src/opennmr/notebook/batch_relaxation.py +190 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/__init__.py +17 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/demag.py +90 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/geometry.py +140 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/io_utils.py +207 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/main.py +251 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/mas_sidebands.py +120 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/packing.py +703 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/shift.py +78 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/simulate.py +90 -0
- opennmr-0.1.0/src/opennmr/notebook/bms/spectrum.py +132 -0
- opennmr-0.1.0/src/opennmr/notebook/bms_spectrum.py +224 -0
- opennmr-0.1.0/src/opennmr/notebook/bridge.py +128 -0
- opennmr-0.1.0/src/opennmr/notebook/combined_fitting.py +998 -0
- opennmr-0.1.0/src/opennmr/notebook/combined_hamiltonian.py +1135 -0
- opennmr-0.1.0/src/opennmr/notebook/cp_buildup.py +525 -0
- opennmr-0.1.0/src/opennmr/notebook/csa_fitting.py +1399 -0
- opennmr-0.1.0/src/opennmr/notebook/csa_relaxation.py +678 -0
- opennmr-0.1.0/src/opennmr/notebook/czjzek.py +1333 -0
- opennmr-0.1.0/src/opennmr/notebook/dft_calibration.py +668 -0
- opennmr-0.1.0/src/opennmr/notebook/dft_nmr.py +1189 -0
- opennmr-0.1.0/src/opennmr/notebook/diffusion.py +153 -0
- opennmr-0.1.0/src/opennmr/notebook/dnp.py +1199 -0
- opennmr-0.1.0/src/opennmr/notebook/dnp_workflow.py +955 -0
- opennmr-0.1.0/src/opennmr/notebook/drift_correction.py +796 -0
- opennmr-0.1.0/src/opennmr/notebook/exchange.py +854 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_advice.py +1523 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_correlation.py +341 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_display.py +167 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_models.py +436 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_ranges.py +79 -0
- opennmr-0.1.0/src/opennmr/notebook/fit_values.py +237 -0
- opennmr-0.1.0/src/opennmr/notebook/fitting.py +1919 -0
- opennmr-0.1.0/src/opennmr/notebook/fonts.py +121 -0
- opennmr-0.1.0/src/opennmr/notebook/intensity_scaling.py +194 -0
- opennmr-0.1.0/src/opennmr/notebook/isotopes.py +173 -0
- opennmr-0.1.0/src/opennmr/notebook/jcoupling_fitting.py +1165 -0
- opennmr-0.1.0/src/opennmr/notebook/manual_baseline.py +394 -0
- opennmr-0.1.0/src/opennmr/notebook/multi_fitting.py +1348 -0
- opennmr-0.1.0/src/opennmr/notebook/notes.py +113 -0
- opennmr-0.1.0/src/opennmr/notebook/offset_tracker.py +742 -0
- opennmr-0.1.0/src/opennmr/notebook/one_d.py +1996 -0
- opennmr-0.1.0/src/opennmr/notebook/peak_width.py +367 -0
- opennmr-0.1.0/src/opennmr/notebook/periodic_table.py +715 -0
- opennmr-0.1.0/src/opennmr/notebook/plotting.py +721 -0
- opennmr-0.1.0/src/opennmr/notebook/pnmr_export.py +307 -0
- opennmr-0.1.0/src/opennmr/notebook/pnmr_fit.py +1881 -0
- opennmr-0.1.0/src/opennmr/notebook/pnmr_simulator.py +1135 -0
- opennmr-0.1.0/src/opennmr/notebook/progress.py +268 -0
- opennmr-0.1.0/src/opennmr/notebook/quadrupolar_fitting.py +787 -0
- opennmr-0.1.0/src/opennmr/notebook/referencing.py +204 -0
- opennmr-0.1.0/src/opennmr/notebook/regularisation.py +798 -0
- opennmr-0.1.0/src/opennmr/notebook/relaxation.py +1233 -0
- opennmr-0.1.0/src/opennmr/notebook/relaxometry.py +30 -0
- opennmr-0.1.0/src/opennmr/notebook/scripts/verify_rotation_convention.py +97 -0
- opennmr-0.1.0/src/opennmr/notebook/series_fitting.py +291 -0
- opennmr-0.1.0/src/opennmr/notebook/shearing.py +1261 -0
- opennmr-0.1.0/src/opennmr/notebook/simpson.py +1094 -0
- opennmr-0.1.0/src/opennmr/notebook/simulations.py +2254 -0
- opennmr-0.1.0/src/opennmr/notebook/snr.py +1538 -0
- opennmr-0.1.0/src/opennmr/notebook/spin_system.py +697 -0
- opennmr-0.1.0/src/opennmr/notebook/start_estimates.py +445 -0
- opennmr-0.1.0/src/opennmr/notebook/state.py +108 -0
- opennmr-0.1.0/src/opennmr/notebook/stored_integrals.py +264 -0
- opennmr-0.1.0/src/opennmr/notebook/tensor_views.py +688 -0
- opennmr-0.1.0/src/opennmr/notebook/two_d.py +3060 -0
- opennmr-0.1.0/src/opennmr/notebook/utils.py +672 -0
- opennmr-0.1.0/src/opennmr/notebook/vt_relaxation.py +1523 -0
- opennmr-0.1.0/src/opennmr/plotting/__init__.py +40 -0
- opennmr-0.1.0/src/opennmr/plotting/accessibility.py +455 -0
- opennmr-0.1.0/src/opennmr/plotting/fit.py +946 -0
- opennmr-0.1.0/src/opennmr/plotting/labels.py +307 -0
- opennmr-0.1.0/src/opennmr/plotting/lines.py +218 -0
- opennmr-0.1.0/src/opennmr/plotting/oned.py +276 -0
- opennmr-0.1.0/src/opennmr/plotting/style.py +1021 -0
- opennmr-0.1.0/src/opennmr/plotting/twod.py +1356 -0
- opennmr-0.1.0/src/opennmr/plotting/vector_export.py +159 -0
- opennmr-0.1.0/src/opennmr/processing/__init__.py +89 -0
- opennmr-0.1.0/src/opennmr/processing/arithmetic.py +136 -0
- opennmr-0.1.0/src/opennmr/processing/diffusion.py +767 -0
- opennmr-0.1.0/src/opennmr/processing/enmr.py +1477 -0
- opennmr-0.1.0/src/opennmr/processing/fitting.py +2123 -0
- opennmr-0.1.0/src/opennmr/processing/ft.py +1142 -0
- opennmr-0.1.0/src/opennmr/processing/ilt.py +343 -0
- opennmr-0.1.0/src/opennmr/processing/refdeconv.py +320 -0
- opennmr-0.1.0/src/opennmr/processing/relaxation.py +2350 -0
- opennmr-0.1.0/src/opennmr/processing/relaxometry.py +578 -0
- opennmr-0.1.0/src/opennmr/processing/report.py +355 -0
- opennmr-0.1.0/src/opennmr/processing/score.py +1007 -0
- opennmr-0.1.0/src/opennmr/processing/slicing.py +313 -0
- opennmr-0.1.0/src/opennmr/processing/vocs.py +342 -0
- opennmr-0.1.0/src/opennmr/processing/windows.py +67 -0
- opennmr-0.1.0/src/opennmr/simulate/__init__.py +6 -0
- opennmr-0.1.0/src/opennmr/simulate/bloch.py +914 -0
- opennmr-0.1.0/src/opennmr/simulate/diffusion.py +144 -0
- opennmr-0.1.0/src/opennmr/topspin/__init__.py +8 -0
- opennmr-0.1.0/src/opennmr/topspin/install.py +486 -0
- opennmr-0.1.0/src/opennmr/topspin/resources/opennmr.py +341 -0
- opennmr-0.1.0/src/opennmr/update.py +121 -0
- opennmr-0.1.0/src/opennmr.egg-info/PKG-INFO +449 -0
- opennmr-0.1.0/src/opennmr.egg-info/SOURCES.txt +404 -0
- opennmr-0.1.0/src/opennmr.egg-info/dependency_links.txt +1 -0
- opennmr-0.1.0/src/opennmr.egg-info/entry_points.txt +3 -0
- opennmr-0.1.0/src/opennmr.egg-info/requires.txt +17 -0
- opennmr-0.1.0/src/opennmr.egg-info/top_level.txt +1 -0
- opennmr-0.1.0/tests/test_accessibility.py +257 -0
- opennmr-0.1.0/tests/test_arithmetic.py +65 -0
- opennmr-0.1.0/tests/test_auto_spline_baseline.py +164 -0
- opennmr-0.1.0/tests/test_background_load.py +154 -0
- opennmr-0.1.0/tests/test_batch_relaxation.py +180 -0
- opennmr-0.1.0/tests/test_bloch_adiabatic.py +232 -0
- opennmr-0.1.0/tests/test_bloch_any_duration.py +111 -0
- opennmr-0.1.0/tests/test_bloch_tanh_tan_review.py +89 -0
- opennmr-0.1.0/tests/test_bruker.py +951 -0
- opennmr-0.1.0/tests/test_bruker_2d_default.py +101 -0
- opennmr-0.1.0/tests/test_bruker_pfg.py +242 -0
- opennmr-0.1.0/tests/test_bruker_raw_2d.py +871 -0
- opennmr-0.1.0/tests/test_bruker_raw_fid.py +364 -0
- opennmr-0.1.0/tests/test_cloud.py +136 -0
- opennmr-0.1.0/tests/test_collapsible_section.py +139 -0
- opennmr-0.1.0/tests/test_colorbar_gap_and_czjzek_labels.py +130 -0
- opennmr-0.1.0/tests/test_combined_fitting.py +138 -0
- opennmr-0.1.0/tests/test_combined_hamiltonian.py +256 -0
- opennmr-0.1.0/tests/test_compare_2d.py +199 -0
- opennmr-0.1.0/tests/test_compare_cross_measure.py +72 -0
- opennmr-0.1.0/tests/test_compare_width_ranges_cp.py +488 -0
- opennmr-0.1.0/tests/test_cpmg.py +227 -0
- opennmr-0.1.0/tests/test_csa_fitting.py +158 -0
- opennmr-0.1.0/tests/test_czjzek.py +288 -0
- opennmr-0.1.0/tests/test_data_browser.py +463 -0
- opennmr-0.1.0/tests/test_dft_calibration.py +228 -0
- opennmr-0.1.0/tests/test_dft_molecular.py +213 -0
- opennmr-0.1.0/tests/test_dft_nmr.py +407 -0
- opennmr-0.1.0/tests/test_diffusion.py +428 -0
- opennmr-0.1.0/tests/test_digital_filter.py +85 -0
- opennmr-0.1.0/tests/test_dnp.py +417 -0
- opennmr-0.1.0/tests/test_dnp_workflow.py +248 -0
- opennmr-0.1.0/tests/test_drift_correction.py +492 -0
- opennmr-0.1.0/tests/test_enmr.py +1161 -0
- opennmr-0.1.0/tests/test_exchange.py +273 -0
- opennmr-0.1.0/tests/test_experiment_notes.py +280 -0
- opennmr-0.1.0/tests/test_external_projection.py +398 -0
- opennmr-0.1.0/tests/test_fact_box.py +95 -0
- opennmr-0.1.0/tests/test_field_proton_typing_and_haeberlen_eta.py +206 -0
- opennmr-0.1.0/tests/test_fit_advice.py +244 -0
- opennmr-0.1.0/tests/test_fit_correlation.py +268 -0
- opennmr-0.1.0/tests/test_fit_mas_rate.py +219 -0
- opennmr-0.1.0/tests/test_fit_progress.py +222 -0
- opennmr-0.1.0/tests/test_fit_values_kept_fits_crosshair_state_folder.py +309 -0
- opennmr-0.1.0/tests/test_fitting.py +627 -0
- opennmr-0.1.0/tests/test_fonts_vector_export_headers.py +234 -0
- opennmr-0.1.0/tests/test_ft.py +1026 -0
- opennmr-0.1.0/tests/test_full_2d_autophase_warning.py +148 -0
- opennmr-0.1.0/tests/test_gui.py +16403 -0
- opennmr-0.1.0/tests/test_gui_adiabatic_tensors.py +178 -0
- opennmr-0.1.0/tests/test_gui_canvas.py +4511 -0
- opennmr-0.1.0/tests/test_gui_combined.py +141 -0
- opennmr-0.1.0/tests/test_gui_combined_consolidation.py +286 -0
- opennmr-0.1.0/tests/test_gui_combined_fit.py +247 -0
- opennmr-0.1.0/tests/test_gui_compare_tools.py +371 -0
- opennmr-0.1.0/tests/test_gui_copy_interactions.py +133 -0
- opennmr-0.1.0/tests/test_gui_csa.py +221 -0
- opennmr-0.1.0/tests/test_gui_curve_fit_plot.py +152 -0
- opennmr-0.1.0/tests/test_gui_czjzek_display.py +329 -0
- opennmr-0.1.0/tests/test_gui_dft.py +298 -0
- opennmr-0.1.0/tests/test_gui_dnp.py +226 -0
- opennmr-0.1.0/tests/test_gui_drift_and_sr_transfer.py +235 -0
- opennmr-0.1.0/tests/test_gui_dynamics.py +309 -0
- opennmr-0.1.0/tests/test_gui_export_scroll.py +160 -0
- opennmr-0.1.0/tests/test_gui_field_and_wheel.py +207 -0
- opennmr-0.1.0/tests/test_gui_fit_advice.py +179 -0
- opennmr-0.1.0/tests/test_gui_fit_sim_state.py +330 -0
- opennmr-0.1.0/tests/test_gui_integration_to_dynamics.py +110 -0
- opennmr-0.1.0/tests/test_gui_isotope_fitting.py +204 -0
- opennmr-0.1.0/tests/test_gui_jcoupling.py +481 -0
- opennmr-0.1.0/tests/test_gui_legend_names.py +192 -0
- opennmr-0.1.0/tests/test_gui_legend_style.py +304 -0
- opennmr-0.1.0/tests/test_gui_manual_baseline.py +310 -0
- opennmr-0.1.0/tests/test_gui_multi_fit.py +278 -0
- opennmr-0.1.0/tests/test_gui_multi_fit_keep_copy.py +165 -0
- opennmr-0.1.0/tests/test_gui_multisite_sim.py +290 -0
- opennmr-0.1.0/tests/test_gui_periodic_table.py +232 -0
- opennmr-0.1.0/tests/test_gui_pnmr.py +555 -0
- opennmr-0.1.0/tests/test_gui_pnmr_export.py +109 -0
- opennmr-0.1.0/tests/test_gui_pnmr_fit.py +413 -0
- opennmr-0.1.0/tests/test_gui_progress.py +425 -0
- opennmr-0.1.0/tests/test_gui_quadrupolar_fit.py +111 -0
- opennmr-0.1.0/tests/test_gui_readable_reports_zoom_site_table.py +297 -0
- opennmr-0.1.0/tests/test_gui_reflow.py +165 -0
- opennmr-0.1.0/tests/test_gui_relaxometry.py +180 -0
- opennmr-0.1.0/tests/test_gui_remove_and_plot_kinds.py +92 -0
- opennmr-0.1.0/tests/test_gui_round4_czjzek.py +120 -0
- opennmr-0.1.0/tests/test_gui_round4_fits.py +262 -0
- opennmr-0.1.0/tests/test_gui_round4_keep_sim.py +87 -0
- opennmr-0.1.0/tests/test_gui_round4_transfers.py +203 -0
- opennmr-0.1.0/tests/test_gui_shearing.py +155 -0
- opennmr-0.1.0/tests/test_gui_simulation_plots.py +363 -0
- opennmr-0.1.0/tests/test_gui_snr.py +189 -0
- opennmr-0.1.0/tests/test_gui_tensor_style.py +415 -0
- opennmr-0.1.0/tests/test_gui_usability.py +176 -0
- opennmr-0.1.0/tests/test_gui_view_and_modes.py +401 -0
- opennmr-0.1.0/tests/test_history.py +361 -0
- opennmr-0.1.0/tests/test_ilt.py +257 -0
- opennmr-0.1.0/tests/test_integrate_tile_plot_options_regularisation.py +349 -0
- opennmr-0.1.0/tests/test_io_base.py +55 -0
- opennmr-0.1.0/tests/test_isotope_fitting.py +283 -0
- opennmr-0.1.0/tests/test_jcampdx.py +105 -0
- opennmr-0.1.0/tests/test_jcoupling.py +193 -0
- opennmr-0.1.0/tests/test_jeol.py +344 -0
- opennmr-0.1.0/tests/test_lebedev_powder.py +166 -0
- opennmr-0.1.0/tests/test_line_styles.py +260 -0
- opennmr-0.1.0/tests/test_lists.py +207 -0
- opennmr-0.1.0/tests/test_magritek_1d_2d.py +248 -0
- opennmr-0.1.0/tests/test_manual_baseline.py +223 -0
- opennmr-0.1.0/tests/test_max_iterations_sidebands_contour_scroll.py +365 -0
- opennmr-0.1.0/tests/test_multi_fit_quantities.py +223 -0
- opennmr-0.1.0/tests/test_multi_fitting.py +235 -0
- opennmr-0.1.0/tests/test_multifit_sites_cp_regions_state_load.py +235 -0
- opennmr-0.1.0/tests/test_multisite_simulation.py +133 -0
- opennmr-0.1.0/tests/test_notebook_bridge.py +109 -0
- opennmr-0.1.0/tests/test_notebook_single_import.py +84 -0
- opennmr-0.1.0/tests/test_one_d.py +396 -0
- opennmr-0.1.0/tests/test_periodic_table.py +160 -0
- opennmr-0.1.0/tests/test_persistence.py +197 -0
- opennmr-0.1.0/tests/test_pfg_wiring.py +387 -0
- opennmr-0.1.0/tests/test_plotting_fit.py +497 -0
- opennmr-0.1.0/tests/test_plotting_oned.py +131 -0
- opennmr-0.1.0/tests/test_plotting_style.py +628 -0
- opennmr-0.1.0/tests/test_plotting_twod.py +701 -0
- opennmr-0.1.0/tests/test_pnmr_export.py +139 -0
- opennmr-0.1.0/tests/test_pnmr_fit.py +298 -0
- opennmr-0.1.0/tests/test_pnmr_simulator.py +530 -0
- opennmr-0.1.0/tests/test_procno_1d_of_2d_and_snr_view.py +154 -0
- opennmr-0.1.0/tests/test_procnos_snr_styles_units_sections.py +224 -0
- opennmr-0.1.0/tests/test_projection_style_and_labels.py +293 -0
- opennmr-0.1.0/tests/test_quadrupolar_fitting.py +163 -0
- opennmr-0.1.0/tests/test_quadrupolar_simulation.py +268 -0
- opennmr-0.1.0/tests/test_refdeconv.py +296 -0
- opennmr-0.1.0/tests/test_referencing.py +57 -0
- opennmr-0.1.0/tests/test_registry.py +104 -0
- opennmr-0.1.0/tests/test_regularisation.py +194 -0
- opennmr-0.1.0/tests/test_relaxation.py +927 -0
- opennmr-0.1.0/tests/test_relaxation_reporting.py +362 -0
- opennmr-0.1.0/tests/test_relaxometry.py +393 -0
- opennmr-0.1.0/tests/test_reopen_keeps_recorded_phase.py +102 -0
- opennmr-0.1.0/tests/test_report.py +130 -0
- opennmr-0.1.0/tests/test_round_2026_10_17.py +510 -0
- opennmr-0.1.0/tests/test_round_2026_10_18.py +303 -0
- opennmr-0.1.0/tests/test_round_2026_10_19.py +556 -0
- opennmr-0.1.0/tests/test_round_2026_10_20.py +254 -0
- opennmr-0.1.0/tests/test_scan_and_procno_limits.py +51 -0
- opennmr-0.1.0/tests/test_score.py +623 -0
- opennmr-0.1.0/tests/test_series_fitting.py +333 -0
- opennmr-0.1.0/tests/test_settings_data_sections_drag.py +255 -0
- opennmr-0.1.0/tests/test_shared_patterns.py +151 -0
- opennmr-0.1.0/tests/test_shearing.py +634 -0
- opennmr-0.1.0/tests/test_sim_csa_axis.py +65 -0
- opennmr-0.1.0/tests/test_simulate.py +152 -0
- opennmr-0.1.0/tests/test_slice_gaps_truncate_calibrate_undo.py +369 -0
- opennmr-0.1.0/tests/test_slice_panel_dblclick_clear_history_compare.py +246 -0
- opennmr-0.1.0/tests/test_slicing.py +152 -0
- opennmr-0.1.0/tests/test_snr.py +228 -0
- opennmr-0.1.0/tests/test_snr_conventions_2d.py +190 -0
- opennmr-0.1.0/tests/test_snr_display_and_drag_cleanup.py +218 -0
- opennmr-0.1.0/tests/test_snr_keeps_compare_colours.py +86 -0
- opennmr-0.1.0/tests/test_spin_system.py +321 -0
- opennmr-0.1.0/tests/test_spinsolve.py +161 -0
- opennmr-0.1.0/tests/test_start_estimates.py +150 -0
- opennmr-0.1.0/tests/test_startup_time.py +223 -0
- opennmr-0.1.0/tests/test_state_and_fit_models.py +348 -0
- opennmr-0.1.0/tests/test_tensor_views.py +165 -0
- opennmr-0.1.0/tests/test_text_files_are_utf8.py +36 -0
- opennmr-0.1.0/tests/test_topspin_install.py +105 -0
- opennmr-0.1.0/tests/test_topspin_processing_2d.py +212 -0
- opennmr-0.1.0/tests/test_tutorial_round_czjzek_series.py +198 -0
- opennmr-0.1.0/tests/test_two_d.py +282 -0
- opennmr-0.1.0/tests/test_update.py +99 -0
- opennmr-0.1.0/tests/test_varian.py +323 -0
- opennmr-0.1.0/tests/test_varian_binary.py +82 -0
- opennmr-0.1.0/tests/test_vocs.py +289 -0
- opennmr-0.1.0/tests/test_vocs_slices.py +101 -0
- opennmr-0.1.0/tests/test_vt_relaxation.py +331 -0
- opennmr-0.1.0/tests/test_welcome_wizard.py +175 -0
- opennmr-0.1.0/tests/test_window_cleanup.py +60 -0
- opennmr-0.1.0/tests/test_windows.py +75 -0
- opennmr-0.1.0/tests/test_wrapping_tabs.py +274 -0
- opennmr-0.1.0/tests/test_xpulse.py +1115 -0
opennmr-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 openNMR contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
opennmr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: opennmr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Vendor-agnostic NMR import, processing, plotting, and GUI
|
|
5
|
+
Author: Euan Bassey, Leo Gordon
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: NMR,solid-state NMR,spectroscopy,fitting
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Topic :: Scientific/Engineering :: Chemistry
|
|
10
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Requires-Python: >=3.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: scipy>=1.15
|
|
17
|
+
Requires-Dist: pandas
|
|
18
|
+
Requires-Dist: matplotlib
|
|
19
|
+
Requires-Dist: PySide6-Essentials
|
|
20
|
+
Requires-Dist: sympy
|
|
21
|
+
Requires-Dist: pymatgen
|
|
22
|
+
Provides-Extra: test
|
|
23
|
+
Requires-Dist: pytest; extra == "test"
|
|
24
|
+
Requires-Dist: pytest-xdist; extra == "test"
|
|
25
|
+
Requires-Dist: pytest-timeout; extra == "test"
|
|
26
|
+
Requires-Dist: nmrglue; extra == "test"
|
|
27
|
+
Requires-Dist: pymatgen; extra == "test"
|
|
28
|
+
Provides-Extra: build
|
|
29
|
+
Requires-Dist: pyinstaller; extra == "build"
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
Vendor-agnostic NMR import, processing, plotting, and a desktop GUI.
|
|
33
|
+
|
|
34
|
+
Every vendor importer reads raw acquisition data (not a vendor's own
|
|
35
|
+
pre-processed output) and produces the same `NMRSpectrum` object, which
|
|
36
|
+
the same processing and plotting code then operates on. Currently
|
|
37
|
+
supported: Bruker (TopSpin `fid`/`ser`), X-Pulse (JCAMP-DX FID, including
|
|
38
|
+
arrayed/PFG experiments), Varian/Agilent (`fid`/`procpar`), JEOL Delta
|
|
39
|
+
(`.jdf`, 1D only so far).
|
|
40
|
+
|
|
41
|
+
## Layout
|
|
42
|
+
|
|
43
|
+
- `opennmr.core` — vendor-agnostic data model (`NMRSpectrum`, `NMRAxis`,
|
|
44
|
+
`NMRMetadata`) and nucleus/gyromagnetic-ratio lookups.
|
|
45
|
+
- `opennmr.io` — per-vendor readers, all producing `NMRSpectrum`.
|
|
46
|
+
- `opennmr.processing` — FID processing (apodization, FFT, phasing),
|
|
47
|
+
VOCS, spectrum arithmetic, 2D slice extraction, peak fitting,
|
|
48
|
+
relaxation and diffusion fitting, PFG b-values and gradient
|
|
49
|
+
calibration, DOSY by inverse Laplace transform, and multivariate
|
|
50
|
+
component resolution (SCORE/OUTSCORE).
|
|
51
|
+
- `opennmr.plotting` — matplotlib plotting, `ax`-first so it embeds
|
|
52
|
+
cleanly in a GUI canvas as well as standalone notebook figures.
|
|
53
|
+
- `opennmr.gui` — PySide6 desktop shell.
|
|
54
|
+
|
|
55
|
+
## Install (editable, into an existing environment)
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
pip install -e ".[test]"
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Run the GUI
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
opennmr-gui
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Add an OpenNMR button to TopSpin
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
opennmr-topspin-install
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Finds the TopSpin installations on the local machine, offers to copy the
|
|
74
|
+
OpenNMR plugin into each installation's `exp/stan/nmr/py/user/` folder,
|
|
75
|
+
and to add the `opennmr` launch command to TopSpin's `autostart.mac`.
|
|
76
|
+
At the TopSpin command line, `opennmr` puts the button on the main
|
|
77
|
+
toolbar and `opennmr -kill` takes it off again.
|
|
78
|
+
|
|
79
|
+
**The plugin records the absolute path of the environment you run the
|
|
80
|
+
installer from**, rather than looking for `opennmr-gui` on PATH when the
|
|
81
|
+
button is clicked. Depending on how TopSpin is launched, shell startup
|
|
82
|
+
files may not be read, so a conda install is invisible to it, and on
|
|
83
|
+
Windows neither conda nor python.org's installer puts itself on PATH by
|
|
84
|
+
default. The cost is that moving or recreating the environment can break
|
|
85
|
+
the button; re-run the installer if it does.
|
|
86
|
+
|
|
87
|
+
`--topspin FOLDER` defines a TopSpin installation directory the search
|
|
88
|
+
missed; `--uninstall` removes the plugin and its autostart entry;
|
|
89
|
+
`--list` shows what was found. Writing into TopSpin's own folder may
|
|
90
|
+
need administrator rights.
|
|
91
|
+
|
|
92
|
+
## Run tests
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
pytest
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Getting good data for diffusion and relaxation analysis
|
|
99
|
+
|
|
100
|
+
The analysis methods in this package differ enormously in how much they
|
|
101
|
+
ask of the data, and **the demanding ones fail quietly**: they return a
|
|
102
|
+
confident-looking answer with an excellent fit statistic. This section
|
|
103
|
+
is what each one actually needs. The GUI repeats the essentials in the
|
|
104
|
+
relevant dialogs.
|
|
105
|
+
|
|
106
|
+
### The single most important thing
|
|
107
|
+
|
|
108
|
+
**Fit quality does not tell you whether a multivariate resolution is
|
|
109
|
+
right.** Measured directly on synthetic mixtures in this repository's
|
|
110
|
+
own test suite: an OUTSCORE run whose diffusion coefficients were **69%
|
|
111
|
+
wrong** reported R² = 0.99940, against 0.99937 for the run that got them
|
|
112
|
+
right. The error surface really is that flat — wildly different answers
|
|
113
|
+
reproduce the data equally well. A good R² means "this many components
|
|
114
|
+
can fit", never "these are the right components".
|
|
115
|
+
|
|
116
|
+
This is why `processing.score` reports cross-talk alongside R², warns
|
|
117
|
+
when the two methods disagree while fitting equally well, and says in
|
|
118
|
+
its own report which number the run actually optimised.
|
|
119
|
+
|
|
120
|
+
### Acquisition
|
|
121
|
+
|
|
122
|
+
| | Peak-by-peak fit | DOSY (ILT) | SCORE / OUTSCORE |
|
|
123
|
+
|---|---|---|---|
|
|
124
|
+
| Gradient levels / delays | 8+ | 12+ | 8–10 minimum, 15+ preferred |
|
|
125
|
+
| Attenuation of the fastest component | to ~20% | to ~5% | to ~10% |
|
|
126
|
+
| Phase / baseline | helps | required | required |
|
|
127
|
+
| Resolves overlapped peaks | no | no | yes |
|
|
128
|
+
|
|
129
|
+
* **Sample the decay, don't just start it.** A component that loses
|
|
130
|
+
only 20% of its intensity across the whole series is nearly a
|
|
131
|
+
constant, and its decay constant is poorly determined no matter how
|
|
132
|
+
good the fit looks. There is a test in `tests/test_score.py` pinning
|
|
133
|
+
this down: a component attenuating by 8% is recovered only to within
|
|
134
|
+
a factor, which is a property of the experiment and not of the
|
|
135
|
+
algorithm. The fix is a longer diffusion time or stronger gradients.
|
|
136
|
+
* **Space the levels sensibly.** Equal steps in *b* (equal steps in
|
|
137
|
+
gradient *squared*) sample a decay far better than equal steps in
|
|
138
|
+
gradient amplitude, which crowds points into the undecayed start.
|
|
139
|
+
* **Keep signal-to-noise up on the last levels too**, not just the
|
|
140
|
+
first. The most-attenuated points carry most of the information about
|
|
141
|
+
the slow components.
|
|
142
|
+
* **Don't let peaks move.** A bilinear model — the assumption behind
|
|
143
|
+
SCORE, OUTSCORE and DOSY alike — says each component has one fixed
|
|
144
|
+
spectrum. A peak that drifts across the series (temperature,
|
|
145
|
+
exchange, referencing) breaks that, and shows up as a spurious extra
|
|
146
|
+
component rather than as an error.
|
|
147
|
+
|
|
148
|
+
### Processing, before any of these methods
|
|
149
|
+
|
|
150
|
+
1. **Phase properly, in absorption mode.** Non-negativity is the single
|
|
151
|
+
most stabilising constraint available to SCORE, OUTSCORE and the
|
|
152
|
+
ILT, and it means nothing on a dispersive spectrum.
|
|
153
|
+
2. **Baseline-correct.** A rolling baseline is not a non-negative
|
|
154
|
+
superposition of component spectra; it will be absorbed into
|
|
155
|
+
whichever component can best accommodate it.
|
|
156
|
+
3. **Reference consistently across the series.**
|
|
157
|
+
4. **Use real b-values.** The GUI refuses to run DOSY or SCORE when the
|
|
158
|
+
x-axis has fallen back to plain slice indices, because the resulting
|
|
159
|
+
"diffusion coefficients" would be in no physical unit while being
|
|
160
|
+
labelled m²/s. Either read them from a Bruker `diff.xml`, or supply
|
|
161
|
+
δ/Δ/gradient and let `processing.diffusion.b_value` compute them —
|
|
162
|
+
and get the **gradient shape factor** right: treating a stock Bruker
|
|
163
|
+
`SMSQ10` sequence as a perfect rectangle overestimates *b* by 23%,
|
|
164
|
+
which lands directly on D.
|
|
165
|
+
|
|
166
|
+
### Where the x-values come from
|
|
167
|
+
|
|
168
|
+
The Curve Fitting sub-tab's **Values** source sets the x-axis for Fit
|
|
169
|
+
Curve, DOSY and SCORE alike — one setting, so the three cannot end up
|
|
170
|
+
fitting the same series against different axes. The line under it
|
|
171
|
+
always shows what the current source resolves to, against the selected
|
|
172
|
+
experiment.
|
|
173
|
+
|
|
174
|
+
| Source | What it reads |
|
|
175
|
+
|---|---|
|
|
176
|
+
| Auto | this experiment's own `vdlist`/`diff.xml`, else slice index |
|
|
177
|
+
| Manual list | values typed in, comma- or whitespace-separated (`10m` etc. accepted) |
|
|
178
|
+
| Start + step | an evenly-spaced series, one value per slice |
|
|
179
|
+
| **List file × factor** | **any one-column text file of numbers, scaled by a constant or an acquisition parameter** |
|
|
180
|
+
| Relative gradient | an X-Pulse arrayed PFG series' own gradient fractions |
|
|
181
|
+
| b from gradients | a true b-value computed from δ/Δ/G |
|
|
182
|
+
|
|
183
|
+
**List file × factor** is the vendor-neutral one. `opennmr.io.lists`
|
|
184
|
+
reads any one-column list — a Bruker `vdlist`/`vclist`/`difflist`, an
|
|
185
|
+
X-Pulse `parameters/…_valueslist.txt`, your own `delays.txt` — and
|
|
186
|
+
`find_value_lists` offers whatever is actually sitting with the data
|
|
187
|
+
(the experiment folder, Bruker's `lists/` tree, X-Pulse's `parameters/`,
|
|
188
|
+
and the parent folder), with Browse… for anything else. Nothing is
|
|
189
|
+
inferred from the filename: the Factor is what turns a loop counter or
|
|
190
|
+
a relative fraction into a physical quantity.
|
|
191
|
+
|
|
192
|
+
The **Factor accepts an expression in the experiment's own acquisition
|
|
193
|
+
parameters**, not only a number — the same names a custom fitting
|
|
194
|
+
equation uses, from `opennmr.core.acquisition.acquisition_constants`
|
|
195
|
+
(`TAU`, `P90`, `D70` for X-Pulse; `d1`, `p1`, `cnst2`, `l4` for
|
|
196
|
+
Bruker). So an X-Pulse CPMG echo-count list becomes a T2 delay series
|
|
197
|
+
with a factor of `TAU*2`, with no arithmetic done by hand. The panel
|
|
198
|
+
shows what the expression resolved to and which parameters went into
|
|
199
|
+
it. Parameters are used **exactly as the vendor recorded them** — an
|
|
200
|
+
X-Pulse `TAU` is in µs and stays in µs, so any conversion belongs in
|
|
201
|
+
the expression; only the person reading the pulse programme knows the
|
|
202
|
+
unit it was written in.
|
|
203
|
+
|
|
204
|
+
Everything in the panel is **per experiment**. Selecting a different
|
|
205
|
+
spectrum re-scans *its* folder for list files and restores *its* own
|
|
206
|
+
factor, unit and source — nothing is carried over from the one before.
|
|
207
|
+
|
|
208
|
+
**Unit.** The Unit combo says what the numbers in this panel *are*,
|
|
209
|
+
not only how they are displayed — a typed list of `10, 20, 40` and a
|
|
210
|
+
list *file* holding `10, 20, 40` both mean 10, 20, 40 of that unit.
|
|
211
|
+
It defaults to **ms**; fits are always done in seconds. For a list
|
|
212
|
+
file it applies to the whole product, list × Factor, which makes the
|
|
213
|
+
X-Pulse CPMG case simpler rather than harder: with the Unit on µs an
|
|
214
|
+
echo-count list takes a factor of plain `TAU*2`, since `TAU` is
|
|
215
|
+
already recorded in µs.
|
|
216
|
+
|
|
217
|
+
Two things are never rescaled by it, because they already carry a unit
|
|
218
|
+
of their own: a value that names one (`10m` is 10 ms whatever the combo
|
|
219
|
+
says — a `vdlist` written that way is taken at its word) and the **Auto**
|
|
220
|
+
source, which is the vendor's own values in the vendor's own unit. A
|
|
221
|
+
diffusion fit has no Unit row at all: a b-value has no comparable
|
|
222
|
+
prefix convention.
|
|
223
|
+
|
|
224
|
+
The unit reaches the **whole** panel, not just the conversion: the Fit
|
|
225
|
+
tab's x-axis and label, the guess table's time rows (in both
|
|
226
|
+
directions — a `T2` guess typed as `50` under a ms heading reaches the
|
|
227
|
+
model as 0.05 s), "adopt fit as guess", the bounds error messages, and
|
|
228
|
+
the CSV export. Only *time* parameters scale —
|
|
229
|
+
`relaxation._TIME_PARAMS`, the same set that decides which reported
|
|
230
|
+
values get a time unit printed after them — so an amplitude, a stretch
|
|
231
|
+
exponent and a diffusion coefficient are left alone. The stored fit is
|
|
232
|
+
never modified; the unit is a display transform, so changing it redraws
|
|
233
|
+
rather than refits.
|
|
234
|
+
|
|
235
|
+
Fitted values in the report keep `format_time_value`'s automatic
|
|
236
|
+
ms/µs/ns scaling rather than being forced to the chosen unit: it is
|
|
237
|
+
never wrong, and pinning it would print a 2 s result as "2000 ms".
|
|
238
|
+
|
|
239
|
+
**Slices.** The Curve Fitting panel's Slices box limits the analysis to
|
|
240
|
+
the first N slices of a series ("all" by default). One setting feeds
|
|
241
|
+
Fit Spectrum, Fit Curve, DOSY and SCORE, so they cannot disagree about
|
|
242
|
+
how much of a series is in use; DOSY and SCORE cut the data matrix with
|
|
243
|
+
it, not only the x-values.
|
|
244
|
+
|
|
245
|
+
A value list **longer** than the data is not an error — the surplus is
|
|
246
|
+
simply unused, the way TopSpin uses the first F1-TD entries of a longer
|
|
247
|
+
`vdlist`. A shorter one cannot be fitted, and says so with the slice
|
|
248
|
+
count to set. Pseudo-2D row counts are capped at `acqu2s`'s own `TD`
|
|
249
|
+
(the *status* parameter file): the floor-division fallback in
|
|
250
|
+
`_guess_raw_2d_shape` only exists to find a reshapable shape, and rows
|
|
251
|
+
past TD are not acquired data.
|
|
252
|
+
|
|
253
|
+
**Relative amplitudes.** Any model with more than one amplitude reports
|
|
254
|
+
each one as a fraction of the total — on the parameter's own row and
|
|
255
|
+
again in a section pairing it with the rate it belongs to
|
|
256
|
+
(`relaxation.amplitude_fractions`). That is the form a multi-component
|
|
257
|
+
result is quoted and compared in; a raw `A1 = 0.0532` is on whatever
|
|
258
|
+
arbitrary intensity scale the spectra were processed on and says
|
|
259
|
+
nothing on its own. The compact per-peak summary and the global fit's
|
|
260
|
+
per-curve block quote amplitudes *only* as shares, since comparing
|
|
261
|
+
curves is the whole reason those blocks exist.
|
|
262
|
+
|
|
263
|
+
Amplitudes are recognised by name — `M0`, `A`/`A1`/`A2`, `I0`/`I0a`/
|
|
264
|
+
`I0b`, `I1`…`In` — the same way `_TIME_PARAMS` decides what gets a time
|
|
265
|
+
unit. A **custom equation gets no ratios**: its parameters are whatever
|
|
266
|
+
you named them, and treating the wrong symbol as an amplitude would be
|
|
267
|
+
worse than showing nothing. A share outside 0–100% (components of
|
|
268
|
+
opposite sign, usually an over-parameterised model) is flagged rather
|
|
269
|
+
than hidden.
|
|
270
|
+
|
|
271
|
+
**Export curve fit (CSV)** writes the data behind the plot — the x-axis
|
|
272
|
+
in the displayed unit, then every peak's measured integral or
|
|
273
|
+
amplitude at each point with the fitted curve beside it
|
|
274
|
+
(`processing.relaxation.curve_fit_to_csv`). Peaks are not assumed to
|
|
275
|
+
share an x-axis: a point excluded from one peak's fit leaves an empty
|
|
276
|
+
cell rather than a number that was never measured.
|
|
277
|
+
|
|
278
|
+
### SCORE fits the same model Fit Curve does
|
|
279
|
+
|
|
280
|
+
SCORE's per-component decay is not a private exponential: it is
|
|
281
|
+
whichever `processing.relaxation.RELAXATION_MODELS` entry the Model
|
|
282
|
+
combo names, evaluated through `processing.score.build_kernel`. So a
|
|
283
|
+
stretched or inversion-recovery model resolves components with a β or
|
|
284
|
+
an inversion efficiency of their own, and a Stejskal-Tanner fit and a
|
|
285
|
+
SCORE resolution of the same series are the same function asked two
|
|
286
|
+
different questions.
|
|
287
|
+
|
|
288
|
+
The model's **amplitude** parameter is pinned at 1 rather than fitted —
|
|
289
|
+
each component's size lives entirely in its resolved spectrum, which is
|
|
290
|
+
solved for linearly, and fitting both would be exactly redundant. Which
|
|
291
|
+
parameter that is gets measured (doubling it doubles the curve), not
|
|
292
|
+
matched against a list of names, so a custom equation works too. A
|
|
293
|
+
model that already describes several populations (a biexponential) has
|
|
294
|
+
no such parameter and is refused with that reason: SCORE's own
|
|
295
|
+
component count is doing that job.
|
|
296
|
+
5. **Calibrate the gradient if you don't know it.** A maximum gradient
|
|
297
|
+
10% wrong makes every D **21%** wrong, with nothing in the fit to
|
|
298
|
+
show for it. `Analysis ▸ Calibrate Gradient…` measures it from a
|
|
299
|
+
standard of known D.
|
|
300
|
+
6. **Check the gradient axis.** A probe's maximum gradient is saved per
|
|
301
|
+
axis, because the three coils of a triple-axis probe are rarely the
|
|
302
|
+
same strength — one number shared between them quietly rescales
|
|
303
|
+
every D measured on the other two. Set the axis alongside the
|
|
304
|
+
maximum; "Save gradient for probe" then stores that axis's value
|
|
305
|
+
and leaves the others alone. It is the same setting the Imaging tab
|
|
306
|
+
shows, and the same number: one experiment, one coil, one maximum.
|
|
307
|
+
|
|
308
|
+
### Reference deconvolution (`processing.refdeconv`)
|
|
309
|
+
|
|
310
|
+
> **Not wired into the GUI, and not usable on a processed spectrum.**
|
|
311
|
+
> It inverse-transforms the spectrum back to its FID, so the input must
|
|
312
|
+
> be the transform of a *causal* FID. A TopSpin `2rr`/`2ii` is not — it
|
|
313
|
+
> carries the digital-filter group delay and has apodisation and
|
|
314
|
+
> baseline correction folded in. Applied to one, it destroys the line
|
|
315
|
+
> (measured: a peak of 2397 spanning 0.0159 ppm became 16 spanning
|
|
316
|
+
> 0.536 ppm). It needs the raw FID path; that wiring does not exist yet.
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
Corrects a lineshape distortion that **every peak shares** — a bad
|
|
320
|
+
shim, a non-spinning sample, or the electrodes in an eNMR cell. Pick an
|
|
321
|
+
isolated resonance, assert the shape it should have had, and the ratio
|
|
322
|
+
of ideal to measured is the distortion; divide it out and every peak is
|
|
323
|
+
corrected at once.
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from opennmr.processing import refdeconv
|
|
327
|
+
result = refdeconv.reference_deconvolve(
|
|
328
|
+
spectrum_row, ppm, region=(-0.2, 0.2), fwhm=0.01) # target width, ppm
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Validated against a constructed truth (build an FID, distort it,
|
|
332
|
+
transform): a pure phase distortion broadening a line from 0.147 to
|
|
333
|
+
0.244 ppm is corrected back to 0.159, **and peaks outside the reference
|
|
334
|
+
region are corrected too** — the method's whole claim. Recovery degrades
|
|
335
|
+
as the distortion destroys more of the FID (0.031 s of surviving signal
|
|
336
|
+
recovers only 0.574 → 0.366); that limit is information, not
|
|
337
|
+
implementation.
|
|
338
|
+
|
|
339
|
+
`REGULARISATIONS` is the method, not a detail — the reference FID decays
|
|
340
|
+
into noise and an undamped reciprocal diverges. `wiener` (default)
|
|
341
|
+
rolls off smoothly; `truncate` is sharper but rings; `none` exists to
|
|
342
|
+
demonstrate the failure.
|
|
343
|
+
|
|
344
|
+
It does **not** fix convection or electro-osmotic flow (per-scan phase
|
|
345
|
+
modulation, not one fixed envelope), needs a genuinely isolated
|
|
346
|
+
reference, and gives you whatever linewidth you ask for — asking for one
|
|
347
|
+
the acquisition cannot support amplifies noise until the fit looks sharp
|
|
348
|
+
and means nothing.
|
|
349
|
+
|
|
350
|
+
### eNMR (`processing.enmr`, and the Analysis ▸ eNMR tab)
|
|
351
|
+
|
|
352
|
+
The phase a resonance picks up under an applied field is linear in the
|
|
353
|
+
voltage: `Δφ = γ·g·δ·Δ·μ·V/d`.
|
|
354
|
+
|
|
355
|
+
**The slope is the result** — it is what the data determines, in rad/V,
|
|
356
|
+
and needs nothing but the data. Converting it to a mobility needs the
|
|
357
|
+
cell's electrode gap and the sequence's gradient and timings, which live
|
|
358
|
+
in a lab notebook rather than any vendor file, so that step is optional:
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
from opennmr.processing import enmr
|
|
362
|
+
phases = enmr.phase_series(rows, ppm, region=(3.18, 3.42))
|
|
363
|
+
|
|
364
|
+
enmr.fit_enmr(voltages, phases) # slope alone
|
|
365
|
+
enmr.fit_enmr(voltages, phases, electrode_gap=0.0385,
|
|
366
|
+
gamma=2 * np.pi * 42.577e6, gradient=0.3,
|
|
367
|
+
delta=1e-3, big_delta=0.1) # ... and a mobility
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Two levels of pairing, and they are different things.**
|
|
371
|
+
|
|
372
|
+
*Within one experiment*, each voltage is acquired at **both
|
|
373
|
+
polarities**. Reversing the field reverses the drift, so
|
|
374
|
+
`(φ₊ − φ₋)/2` doubles the signal and cancels every phase error that is
|
|
375
|
+
not field-driven. `polarity_pairs` detects this rather than assuming
|
|
376
|
+
it.
|
|
377
|
+
|
|
378
|
+
*Across experiments*, the whole run is repeated with the **gradient
|
|
379
|
+
reversed**, which flips the sign of the fitted slope. `combine_experiments`
|
|
380
|
+
aligns the signs and averages — a plain average would cancel the
|
|
381
|
+
measurement away — and reports the `spread`, because two runs of one
|
|
382
|
+
sample agreeing is the evidence that either measured anything. On a real
|
|
383
|
+
reversed pair: +1.244e-2 and −1.396e-2 → **+1.320e-2 ± 1.5e-3 rad/V**,
|
|
384
|
+
spread 8%.
|
|
385
|
+
|
|
386
|
+
**Unwrap with `phase_ramp`, not `phase_series`.** Consecutive points of
|
|
387
|
+
a +V/−V series are opposite polarity and legitimately far apart, so
|
|
388
|
+
unwrapping across them makes `np.unwrap` read a polarity jump as a wrap
|
|
389
|
+
and insert a 2π step that was never there. On real data that turned a
|
|
390
|
+
clean ramp (R² 0.88) into no correlation at all (R² 0.009) — it does not
|
|
391
|
+
look like a broken calculation, it looks like a negative result.
|
|
392
|
+
`phase_ramp` takes the voltages and picks the stride itself.
|
|
393
|
+
|
|
394
|
+
**Validated against an accepted literature value**, the only check that
|
|
395
|
+
catches a wrong 2π, a wrong drift time or a unit slip, all of which
|
|
396
|
+
still produce a beautiful straight line. On a TMABr standard
|
|
397
|
+
(`test_data/eNMR/...2024-05-13/21`):
|
|
398
|
+
|
|
399
|
+
| species | slope | R² | μ |
|
|
400
|
+
|---|---|---|---|
|
|
401
|
+
| TMA⁺ | 7.90e-3 ± 2.2e-4 rad/V | 0.9954 | **3.79e-8 m²/Vs** (lit. 3.79e-8) |
|
|
402
|
+
| water | 3.3e-5 ± 8.8e-5 rad/V | 0.023 | zero to within error |
|
|
403
|
+
|
|
404
|
+
Water sits in the same tube, field and shim, so it controls for
|
|
405
|
+
everything except charge.
|
|
406
|
+
|
|
407
|
+
Phase comes from the **complex integral** over the peak region, which on
|
|
408
|
+
this data gives R² = 0.995 with no lineshape fit at all; a full complex
|
|
409
|
+
fit is for *overlapped* peaks, not for phase accuracy as such.
|
|
410
|
+
|
|
411
|
+
**The PFG constants read themselves.** `pfg_params(spectrum)` — or
|
|
412
|
+
`fit_enmr(..., spectrum=spectrum)` — fills in γ, g, δ and Δ, so only the
|
|
413
|
+
electrode gap needs typing. Which source each comes from was measured
|
|
414
|
+
across five real datasets, because the pulse programme's legend is not
|
|
415
|
+
enough: `Δ = d20` reproduces `diff.xml` exactly, but `δ` does not
|
|
416
|
+
(`d17+d18` is 67–83% out on four of five), and the gradient in T/m needs
|
|
417
|
+
the probe calibration that only `diff.xml`/`diffConf.xml` carries. So
|
|
418
|
+
`diff.xml` is the source of record, `d20` the cross-check, and each
|
|
419
|
+
value reports its provenance. γ is converted to rad/s/T once, on the way
|
|
420
|
+
out.
|
|
421
|
+
|
|
422
|
+
`electrode_gap` has no default, and a half-supplied set of constants is
|
|
423
|
+
rejected rather than silently skipped: μ scales directly with the gap,
|
|
424
|
+
no vendor file records it, and a plausible-looking default would quietly
|
|
425
|
+
rescale every result.
|
|
426
|
+
|
|
427
|
+
### Choosing a method
|
|
428
|
+
|
|
429
|
+
* **One clean, well-separated peak per species** → ordinary curve
|
|
430
|
+
fitting (`Analysis ▸ Curve Fitting`). It is the only one of these
|
|
431
|
+
that gives a diffusion coefficient with a meaningful uncertainty.
|
|
432
|
+
* **Peaks belonging to the same molecule** → tick *Fit all peaks
|
|
433
|
+
together* and share D. Fitting separately and averaging understates
|
|
434
|
+
the uncertainty.
|
|
435
|
+
* **Overlapped peaks, species differing ≳30% in D** → **SCORE**.
|
|
436
|
+
* **Overlapped peaks, species differing by only a few percent in D** →
|
|
437
|
+
**OUTSCORE**, which minimises cross-talk rather than residual. It
|
|
438
|
+
separates down to ~3% where SCORE needs ~30% — but it assumes the
|
|
439
|
+
components have genuinely *different* peaks. For species that legitimately
|
|
440
|
+
share most of their resonances, minimising overlap pushes toward
|
|
441
|
+
something untrue; the report warns when that has happened.
|
|
442
|
+
* **Unknown number of components, or a continuous distribution** →
|
|
443
|
+
the **DOSY map** (`Analysis ▸ DOSY Map (ILT)`). Treat peak *positions*
|
|
444
|
+
as the result and peak *widths* as an artefact of the regularisation.
|
|
445
|
+
|
|
446
|
+
Both `SCORE`/`OUTSCORE` and the curve fits take the **number of
|
|
447
|
+
components as an input, not an output**. Run at one more and one fewer
|
|
448
|
+
and prefer the answer whose component spectra look like plausible,
|
|
449
|
+
separate molecules.
|