qmfcalc 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.
- qmfcalc-0.1.0/.github/workflows/publish-pypi.yml +88 -0
- qmfcalc-0.1.0/.github/workflows/python-package.yml +43 -0
- qmfcalc-0.1.0/.gitignore +7 -0
- qmfcalc-0.1.0/.gitmodules +3 -0
- qmfcalc-0.1.0/CMakeLists.txt +88 -0
- qmfcalc-0.1.0/PKG-INFO +71 -0
- qmfcalc-0.1.0/README.md +60 -0
- qmfcalc-0.1.0/core/CMakeLists.txt +46 -0
- qmfcalc-0.1.0/core/include/qmfcalc/ion_config.h +65 -0
- qmfcalc-0.1.0/core/include/qmfcalc/matrix_method_calculator.h +43 -0
- qmfcalc-0.1.0/core/include/qmfcalc/resolution_estimator.h +49 -0
- qmfcalc-0.1.0/core/include/qmfcalc/result.h +26 -0
- qmfcalc-0.1.0/core/include/qmfcalc/rod_config.h +20 -0
- qmfcalc-0.1.0/core/include/qmfcalc/stability_diagram.h +57 -0
- qmfcalc-0.1.0/core/include/qmfcalc/voltage_calculator.h +62 -0
- qmfcalc-0.1.0/core/matrix_method_calculator.cpp +57 -0
- qmfcalc-0.1.0/core/resolution_estimator.cpp +126 -0
- qmfcalc-0.1.0/core/voltage_calculator.cpp +64 -0
- qmfcalc-0.1.0/doc/CMakeLists.txt +11 -0
- qmfcalc-0.1.0/doc/command.md +97 -0
- qmfcalc-0.1.0/exec/CMakeLists.txt +23 -0
- qmfcalc-0.1.0/exec/analyze_resolution.cc +289 -0
- qmfcalc-0.1.0/exec/calculate_voltage.cc +246 -0
- qmfcalc-0.1.0/exec/qmf_calc.cpp +349 -0
- qmfcalc-0.1.0/pyproject.toml +28 -0
- qmfcalc-0.1.0/python/CMakeLists.txt +20 -0
- qmfcalc-0.1.0/python/qmfcalc_pybind.cpp +91 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
sdist:
|
|
9
|
+
name: Build source distribution
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
steps:
|
|
13
|
+
- name: Check out source
|
|
14
|
+
uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- name: Set up Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.13"
|
|
20
|
+
|
|
21
|
+
- name: Build sdist
|
|
22
|
+
run: |
|
|
23
|
+
python -m pip install --upgrade pip build
|
|
24
|
+
python -m build --sdist --outdir dist
|
|
25
|
+
|
|
26
|
+
- name: Upload sdist
|
|
27
|
+
uses: actions/upload-artifact@v4
|
|
28
|
+
with:
|
|
29
|
+
name: qmfcalc-sdist
|
|
30
|
+
path: dist/*.tar.gz
|
|
31
|
+
|
|
32
|
+
wheels:
|
|
33
|
+
name: Build wheel
|
|
34
|
+
runs-on: ${{ matrix.os }}
|
|
35
|
+
strategy:
|
|
36
|
+
fail-fast: false
|
|
37
|
+
matrix:
|
|
38
|
+
os: [ubuntu-latest, windows-latest]
|
|
39
|
+
|
|
40
|
+
steps:
|
|
41
|
+
- name: Check out source
|
|
42
|
+
uses: actions/checkout@v4
|
|
43
|
+
|
|
44
|
+
- name: Set up Python
|
|
45
|
+
uses: actions/setup-python@v5
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.13"
|
|
48
|
+
|
|
49
|
+
- name: Build wheel
|
|
50
|
+
uses: pypa/cibuildwheel@v4.1.0
|
|
51
|
+
env:
|
|
52
|
+
CIBW_BUILD: cp313-*
|
|
53
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
54
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
55
|
+
CIBW_SKIP: "*-musllinux_*"
|
|
56
|
+
CIBW_TEST_COMMAND: >-
|
|
57
|
+
python -c "import qmfcalc; r = qmfcalc.calculate_voltages(15.0, 250.0, 250.0, 1.0); assert r['rf_voltage_v'] > 0.0; assert r['dc_voltage_v'] > 0.0; print(r)"
|
|
58
|
+
with:
|
|
59
|
+
output-dir: dist
|
|
60
|
+
|
|
61
|
+
- name: Upload wheel
|
|
62
|
+
uses: actions/upload-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: qmfcalc-${{ matrix.os }}-py3.13-wheel
|
|
65
|
+
path: dist/*.whl
|
|
66
|
+
|
|
67
|
+
publish:
|
|
68
|
+
name: Publish distributions to PyPI
|
|
69
|
+
runs-on: ubuntu-latest
|
|
70
|
+
needs: [sdist, wheels]
|
|
71
|
+
environment: pypi
|
|
72
|
+
permissions:
|
|
73
|
+
id-token: write
|
|
74
|
+
contents: read
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- name: Download distributions
|
|
78
|
+
uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
pattern: qmfcalc-*
|
|
81
|
+
merge-multiple: true
|
|
82
|
+
path: dist
|
|
83
|
+
|
|
84
|
+
- name: List distributions
|
|
85
|
+
run: ls -l dist
|
|
86
|
+
|
|
87
|
+
- name: Publish to PyPI
|
|
88
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Python package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
wheel:
|
|
10
|
+
name: Build wheel
|
|
11
|
+
runs-on: ${{ matrix.os }}
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
os: [ubuntu-latest, windows-latest]
|
|
16
|
+
python-version: ["3.13"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- name: Check out source
|
|
20
|
+
uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Build wheel
|
|
28
|
+
uses: pypa/cibuildwheel@v4.1.0
|
|
29
|
+
env:
|
|
30
|
+
CIBW_BUILD: cp313-*
|
|
31
|
+
CIBW_ARCHS_LINUX: x86_64
|
|
32
|
+
CIBW_ARCHS_WINDOWS: AMD64
|
|
33
|
+
CIBW_SKIP: "*-musllinux_*"
|
|
34
|
+
CIBW_TEST_COMMAND: >-
|
|
35
|
+
python -c "import qmfcalc; r = qmfcalc.calculate_voltages(15.0, 250.0, 250.0, 1.0); assert r['rf_voltage_v'] > 0.0; assert r['dc_voltage_v'] > 0.0; print(r)"
|
|
36
|
+
with:
|
|
37
|
+
output-dir: wheelhouse
|
|
38
|
+
|
|
39
|
+
- name: Upload wheel
|
|
40
|
+
uses: actions/upload-artifact@v4
|
|
41
|
+
with:
|
|
42
|
+
name: qmfcalc-${{ matrix.os }}-py${{ matrix.python-version }}-wheel
|
|
43
|
+
path: wheelhouse/*.whl
|
qmfcalc-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# -----
|
|
2
|
+
# Fundamental settings
|
|
3
|
+
# -----
|
|
4
|
+
|
|
5
|
+
cmake_minimum_required(VERSION 3.28 FATAL_ERROR)
|
|
6
|
+
|
|
7
|
+
project(qmfcalc VERSION 0.1.0 LANGUAGES CXX)
|
|
8
|
+
|
|
9
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
10
|
+
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|
11
|
+
|
|
12
|
+
option(BUILD_SHARED_LIBS "Build using shared libraries" OFF)
|
|
13
|
+
option(QMFCALC_BUILD_EXECUTABLES "Build ROOT-backed command line executables" ON)
|
|
14
|
+
option(QMFCALC_BUILD_DOCS "Build Doxygen documentation target" ON)
|
|
15
|
+
option(QMFCALC_BUILD_PYTHON "Build Python extension module" OFF)
|
|
16
|
+
|
|
17
|
+
if(QMFCALC_BUILD_PYTHON)
|
|
18
|
+
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
|
19
|
+
endif()
|
|
20
|
+
|
|
21
|
+
if(NOT CMAKE_BUILD_TYPE)
|
|
22
|
+
set(CMAKE_BUILD_TYPE Release)
|
|
23
|
+
endif()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# -----
|
|
27
|
+
# External packages
|
|
28
|
+
# -----
|
|
29
|
+
|
|
30
|
+
include(FetchContent)
|
|
31
|
+
|
|
32
|
+
if(QMFCALC_BUILD_EXECUTABLES)
|
|
33
|
+
# argparse: command line argument parsing (header-only).
|
|
34
|
+
FetchContent_Declare(
|
|
35
|
+
argparse
|
|
36
|
+
GIT_REPOSITORY https://github.com/p-ranav/argparse.git
|
|
37
|
+
GIT_TAG v3.2
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# fkYAML: YAML serialization for result output (header-only).
|
|
41
|
+
FetchContent_Declare(
|
|
42
|
+
fkYAML
|
|
43
|
+
GIT_REPOSITORY https://github.com/fktn-k/fkYAML.git
|
|
44
|
+
GIT_TAG v0.4.2
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
FetchContent_MakeAvailable(argparse fkYAML)
|
|
48
|
+
|
|
49
|
+
find_package(ROOT REQUIRED COMPONENTS Core Hist Gpad Tree RIO)
|
|
50
|
+
|
|
51
|
+
# roothelper (CERN ROOT data management and plot formatting) provides the
|
|
52
|
+
# RootHelper INTERFACE target. Its own CMake calls find_package(ROOT).
|
|
53
|
+
add_subdirectory(${PROJECT_SOURCE_DIR}/ext/roothelper)
|
|
54
|
+
endif()
|
|
55
|
+
|
|
56
|
+
if(QMFCALC_BUILD_PYTHON)
|
|
57
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
58
|
+
set(PYBIND11_FINDPYTHON ON)
|
|
59
|
+
find_package(pybind11 CONFIG QUIET)
|
|
60
|
+
|
|
61
|
+
if(NOT pybind11_FOUND)
|
|
62
|
+
FetchContent_Declare(
|
|
63
|
+
pybind11
|
|
64
|
+
GIT_REPOSITORY https://github.com/pybind/pybind11.git
|
|
65
|
+
GIT_TAG v2.13.6
|
|
66
|
+
)
|
|
67
|
+
FetchContent_MakeAvailable(pybind11)
|
|
68
|
+
endif()
|
|
69
|
+
endif()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# -----
|
|
73
|
+
# Sub-directories
|
|
74
|
+
# -----
|
|
75
|
+
|
|
76
|
+
add_subdirectory(${PROJECT_SOURCE_DIR}/core)
|
|
77
|
+
|
|
78
|
+
if(QMFCALC_BUILD_DOCS)
|
|
79
|
+
add_subdirectory(${PROJECT_SOURCE_DIR}/doc)
|
|
80
|
+
endif()
|
|
81
|
+
|
|
82
|
+
if(QMFCALC_BUILD_EXECUTABLES)
|
|
83
|
+
add_subdirectory(${PROJECT_SOURCE_DIR}/exec)
|
|
84
|
+
endif()
|
|
85
|
+
|
|
86
|
+
if(QMFCALC_BUILD_PYTHON)
|
|
87
|
+
add_subdirectory(${PROJECT_SOURCE_DIR}/python)
|
|
88
|
+
endif()
|
qmfcalc-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: qmfcalc
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python bindings for quadrupole mass filter voltage calculations
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
7
|
+
Classifier: Programming Language :: C++
|
|
8
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
9
|
+
Requires-Python: >=3.13
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# qmfcalc
|
|
13
|
+
|
|
14
|
+
Quadrupole mass filter calculation utilities.
|
|
15
|
+
|
|
16
|
+
## Python
|
|
17
|
+
|
|
18
|
+
The Python package builds a native extension with `pybind11`:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
python -m pip install .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Python 3.13 is the supported target.
|
|
25
|
+
|
|
26
|
+
or directly from a Git checkout:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
python -m pip install git+https://github.com/<owner>/<repo>.git
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use it from Python:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import qmfcalc
|
|
36
|
+
|
|
37
|
+
result = qmfcalc.calculate_voltages(
|
|
38
|
+
r0_mm=15.0,
|
|
39
|
+
target_m_over_q_amu_per_z=250.0,
|
|
40
|
+
resolution=250.0,
|
|
41
|
+
rf_frequency_mhz=1.0,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
print(result["rf_voltage_v"], result["dc_voltage_v"])
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`rf_voltage_v` is the per-rod peak-to-peak RF voltage. `dc_voltage_v` is the
|
|
48
|
+
pole-to-pole DC voltage.
|
|
49
|
+
|
|
50
|
+
The GitHub Actions workflow in `.github/workflows/python-package.yml` builds and
|
|
51
|
+
tests Python 3.13 Linux and Windows wheels on pushes and pull requests.
|
|
52
|
+
|
|
53
|
+
## PyPI release
|
|
54
|
+
|
|
55
|
+
Configure a PyPI pending trusted publisher with:
|
|
56
|
+
|
|
57
|
+
- PyPI project name: `qmfcalc`
|
|
58
|
+
- Owner: `kenji0923`
|
|
59
|
+
- Repository: `qmfcalc`
|
|
60
|
+
- Workflow name: `publish-pypi.yml`
|
|
61
|
+
- Environment name: `pypi`
|
|
62
|
+
|
|
63
|
+
Then publish a GitHub Release from a version tag, for example:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
git tag v0.1.0
|
|
67
|
+
git push origin v0.1.0
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The `.github/workflows/publish-pypi.yml` workflow builds the Python 3.13 Linux
|
|
71
|
+
and Windows wheels, builds the source distribution, and publishes them to PyPI.
|
qmfcalc-0.1.0/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# qmfcalc
|
|
2
|
+
|
|
3
|
+
Quadrupole mass filter calculation utilities.
|
|
4
|
+
|
|
5
|
+
## Python
|
|
6
|
+
|
|
7
|
+
The Python package builds a native extension with `pybind11`:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
python -m pip install .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Python 3.13 is the supported target.
|
|
14
|
+
|
|
15
|
+
or directly from a Git checkout:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
python -m pip install git+https://github.com/<owner>/<repo>.git
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Use it from Python:
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import qmfcalc
|
|
25
|
+
|
|
26
|
+
result = qmfcalc.calculate_voltages(
|
|
27
|
+
r0_mm=15.0,
|
|
28
|
+
target_m_over_q_amu_per_z=250.0,
|
|
29
|
+
resolution=250.0,
|
|
30
|
+
rf_frequency_mhz=1.0,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
print(result["rf_voltage_v"], result["dc_voltage_v"])
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`rf_voltage_v` is the per-rod peak-to-peak RF voltage. `dc_voltage_v` is the
|
|
37
|
+
pole-to-pole DC voltage.
|
|
38
|
+
|
|
39
|
+
The GitHub Actions workflow in `.github/workflows/python-package.yml` builds and
|
|
40
|
+
tests Python 3.13 Linux and Windows wheels on pushes and pull requests.
|
|
41
|
+
|
|
42
|
+
## PyPI release
|
|
43
|
+
|
|
44
|
+
Configure a PyPI pending trusted publisher with:
|
|
45
|
+
|
|
46
|
+
- PyPI project name: `qmfcalc`
|
|
47
|
+
- Owner: `kenji0923`
|
|
48
|
+
- Repository: `qmfcalc`
|
|
49
|
+
- Workflow name: `publish-pypi.yml`
|
|
50
|
+
- Environment name: `pypi`
|
|
51
|
+
|
|
52
|
+
Then publish a GitHub Release from a version tag, for example:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
git tag v0.1.0
|
|
56
|
+
git push origin v0.1.0
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The `.github/workflows/publish-pypi.yml` workflow builds the Python 3.13 Linux
|
|
60
|
+
and Windows wheels, builds the source distribution, and publishes them to PyPI.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# ----- Custom function definition start -----
|
|
2
|
+
|
|
3
|
+
# Add the standard include directory.
|
|
4
|
+
function(add_include_directory_std target_name scope)
|
|
5
|
+
target_include_directories(${target_name} ${scope}
|
|
6
|
+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
|
7
|
+
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include/${PROJECT_NAME}>
|
|
8
|
+
)
|
|
9
|
+
endfunction()
|
|
10
|
+
|
|
11
|
+
# Add library with a standard filenames
|
|
12
|
+
# The third argument can be used for specifying library type (STATIC|SHARED|INTERFACE)
|
|
13
|
+
function(add_library_std target_name library_file_name)
|
|
14
|
+
set(library_type "")
|
|
15
|
+
|
|
16
|
+
if(${ARGC} GREATER 2)
|
|
17
|
+
set(library_type ${ARGV2})
|
|
18
|
+
endif()
|
|
19
|
+
|
|
20
|
+
set(include_scope "PUBLIC")
|
|
21
|
+
|
|
22
|
+
if("${library_type}" STREQUAL "INTERFACE")
|
|
23
|
+
add_library(${target_name} ${library_type} include/${PROJECT_NAME}/${library_file_name}.h)
|
|
24
|
+
set(include_scope "INTERFACE")
|
|
25
|
+
else()
|
|
26
|
+
add_library(${target_name} ${library_type} ${library_file_name}.cpp include/${PROJECT_NAME}/${library_file_name}.h)
|
|
27
|
+
endif()
|
|
28
|
+
|
|
29
|
+
add_include_directory_std(${target_name} ${include_scope})
|
|
30
|
+
|
|
31
|
+
add_library(${PROJECT_NAME}::${target_name} ALIAS ${target_name})
|
|
32
|
+
endfunction()
|
|
33
|
+
|
|
34
|
+
# ----- Function definiion end -----
|
|
35
|
+
|
|
36
|
+
add_library_std(StabilityDiagram stability_diagram INTERFACE)
|
|
37
|
+
|
|
38
|
+
add_library_std(ResolutionEstimator resolution_estimator)
|
|
39
|
+
target_link_libraries(ResolutionEstimator PUBLIC
|
|
40
|
+
StabilityDiagram
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
add_library_std(VoltageCalculator voltage_calculator)
|
|
44
|
+
target_link_libraries(VoltageCalculator PUBLIC
|
|
45
|
+
ResolutionEstimator
|
|
46
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#ifndef QMFCALC_ION_CONFIG_H
|
|
2
|
+
#define QMFCALC_ION_CONFIG_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#include <vector>
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
namespace qmfcalc {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
struct IonMotion
|
|
12
|
+
{
|
|
13
|
+
IonMotion(
|
|
14
|
+
const double x,
|
|
15
|
+
const double y,
|
|
16
|
+
const double v_x,
|
|
17
|
+
const double v_y,
|
|
18
|
+
const double v_z
|
|
19
|
+
)
|
|
20
|
+
: x(x),
|
|
21
|
+
y(y),
|
|
22
|
+
v_x(v_x),
|
|
23
|
+
v_y(v_y),
|
|
24
|
+
v_z(v_z)
|
|
25
|
+
{ }
|
|
26
|
+
|
|
27
|
+
double x;
|
|
28
|
+
double y;
|
|
29
|
+
double v_x;
|
|
30
|
+
double v_y;
|
|
31
|
+
double v_z;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
struct IonSet
|
|
36
|
+
{
|
|
37
|
+
IonSet(
|
|
38
|
+
const double mass,
|
|
39
|
+
const std::vector<IonMotion>& motions
|
|
40
|
+
)
|
|
41
|
+
: mass(mass),
|
|
42
|
+
motions(motions)
|
|
43
|
+
{ }
|
|
44
|
+
|
|
45
|
+
const double mass;
|
|
46
|
+
const std::vector<IonMotion> motions;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
struct PhaseSpacePoint
|
|
51
|
+
{
|
|
52
|
+
static double get_u_dot(const double omega, const double v) { return 2. / omega * v; }
|
|
53
|
+
|
|
54
|
+
PhaseSpacePoint(const double omega, const double u, const double v) : omega(omega), u(u), u_dot(get_u_dot(omega, v)) { }
|
|
55
|
+
|
|
56
|
+
const double omega;
|
|
57
|
+
double u;
|
|
58
|
+
double u_dot;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
} // namespace qmfcalc
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
#endif
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#ifndef QMFCALC_MATRIX_METHOD_CALCULATOR_H
|
|
2
|
+
#define QMFCALC_MATRIX_METHOD_CALCULATOR_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#include <array>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
#include <qmfcalc/ion_config.h>
|
|
9
|
+
#include <qmfcalc/result.h>
|
|
10
|
+
#include <qmfcalc/rod_config.h>
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
namespace qmfcalc {
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MatrixMethodCalculator
|
|
17
|
+
{
|
|
18
|
+
public:
|
|
19
|
+
// double get_efficiency(const double mass, const Result::PhaseSpacePoint& initial_point) const;
|
|
20
|
+
|
|
21
|
+
double get_u_max_infinite_length(
|
|
22
|
+
const double mass,
|
|
23
|
+
const IonMotion& ion_motion,
|
|
24
|
+
const double rod_length
|
|
25
|
+
) const;
|
|
26
|
+
|
|
27
|
+
std::vector<Result::Efficiency> get_efficiency_mass_spectrum_infinite_length(
|
|
28
|
+
const std::array<double, 2>& mass_range,
|
|
29
|
+
const double mass_step,
|
|
30
|
+
const std::vector<IonMotion>& ion_motions,
|
|
31
|
+
const RodConfig& rod_config
|
|
32
|
+
) const;
|
|
33
|
+
|
|
34
|
+
private:
|
|
35
|
+
const double RF_omega_;
|
|
36
|
+
const double RF_period_;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
} // namespace qmfcalc
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
#endif
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#ifndef QMFCALC_RESOLUTION_ESTIMATOR_H
|
|
2
|
+
#define QMFCALC_RESOLUTION_ESTIMATOR_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
namespace qmfcalc {
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
// Result of estimating the mass resolution for a single operating line.
|
|
9
|
+
//
|
|
10
|
+
// In a quadrupole mass filter every mass sits on a scan line through the origin
|
|
11
|
+
// of the (q, a) diagram with slope k = a/q = 2U/V (mass independent for fixed
|
|
12
|
+
// rod voltages, frequency and geometry). The line crosses the tip of the first
|
|
13
|
+
// stability region at q_low (rising a0 branch) and q_high (falling b1 branch).
|
|
14
|
+
// Because q is proportional to 1/m, the transmitted mass band corresponds to the
|
|
15
|
+
// q-window delta_q = q_high - q_low, giving m/dm = q_center / delta_q.
|
|
16
|
+
struct ResolutionEstimate
|
|
17
|
+
{
|
|
18
|
+
bool transmitted; // false if the scan line clears the apex (no window)
|
|
19
|
+
double slope; // k = a/q of the operating line
|
|
20
|
+
double q_low; // crossing on the rising a0 branch
|
|
21
|
+
double q_high; // crossing on the falling b1 branch
|
|
22
|
+
double q_center; // (q_low + q_high) / 2 : transmitted mass center in q
|
|
23
|
+
double a_center; // slope * q_center
|
|
24
|
+
double delta_q; // q_high - q_low
|
|
25
|
+
double mass_resolution; // m/dm = q_center / delta_q
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
// Estimate the mass resolution from the line through the origin and (q, a).
|
|
30
|
+
// Only the slope a/q matters; q is required to be non-zero.
|
|
31
|
+
ResolutionEstimate estimate_mass_resolution(double q, double a);
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
// Estimate the mass resolution directly from the operating-line slope k = a/q.
|
|
35
|
+
ResolutionEstimate estimate_mass_resolution_from_slope(double slope);
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
// Inverse of the estimator: the operating-line slope (i.e. the 2U/V ratio)
|
|
39
|
+
// whose window yields target_resolution. The resolution increases monotonically
|
|
40
|
+
// as the slope approaches the apex slope, so a bisection on (0, apex slope) is
|
|
41
|
+
// used. Returns a negative value if target_resolution is below the minimum
|
|
42
|
+
// achievable. This is the kernel of the future RF/DC amplitude solver.
|
|
43
|
+
double find_slope_for_mass_resolution(double target_resolution);
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
} // namespace qmfcalc
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
#endif
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#ifndef QMFCALC_RESULT_H
|
|
2
|
+
#define QMFCALC_RESULT_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
namespace qmfcalc {
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
namespace Result {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
struct Efficiency
|
|
12
|
+
{
|
|
13
|
+
Efficiency(const double mass, const double transmission) : mass(mass), transmission(transmission) { }
|
|
14
|
+
|
|
15
|
+
const double mass;
|
|
16
|
+
const double transmission;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
} // namespace qmfcalc::Result
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
} // namespace qmfcalc
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#endif
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#ifndef QMFCALC_ROD_CONFIG_H
|
|
2
|
+
#define QMFCALC_ROD_CONFIG_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
namespace qmfcalc {
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
struct RodConfig
|
|
9
|
+
{
|
|
10
|
+
RodConfig(const double u_max_acceptable, const double length) : u_max_acceptable(u_max_acceptable), length(length) { }
|
|
11
|
+
|
|
12
|
+
const double u_max_acceptable;
|
|
13
|
+
const double length;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
} // namespace qmfcalc
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
#endif
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#ifndef QMFCALC_STABILITY_DIAGRAM_H
|
|
2
|
+
#define QMFCALC_STABILITY_DIAGRAM_H
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#include <cmath>
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
namespace qmfcalc {
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
// q value of the apex (tip) of the first stability region, where the rising
|
|
12
|
+
// a0 branch and the falling b1 branch meet.
|
|
13
|
+
constexpr double kFirstStabilityQPeak = 0.706252;
|
|
14
|
+
|
|
15
|
+
// q value where the falling b1 branch returns to a = 0 (right end of the tip).
|
|
16
|
+
constexpr double kFirstStabilityQMax = 0.909104;
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
// Upper boundary of the first stability region of the Mathieu equation as a
|
|
20
|
+
// single-valued function of q. The region's tip is bounded by two branches that
|
|
21
|
+
// meet at (kFirstStabilityQPeak, apex):
|
|
22
|
+
// - q < q_peak : rising a0 characteristic curve
|
|
23
|
+
// - q >= q_peak : falling b1 characteristic curve
|
|
24
|
+
// Asymptotic series expansions are used for each branch.
|
|
25
|
+
inline double get_first_stability_boundary(const double q, const double q_peak = kFirstStabilityQPeak)
|
|
26
|
+
{
|
|
27
|
+
const double q2 = std::pow(q, 2);
|
|
28
|
+
const double q4 = std::pow(q, 4);
|
|
29
|
+
const double q6 = std::pow(q, 6);
|
|
30
|
+
const double q8 = std::pow(q, 8);
|
|
31
|
+
|
|
32
|
+
if (q < q_peak) {
|
|
33
|
+
const double a0 = -q2 / 2 + 7 * q4 / 128 - 29 * q6 / 2304 + 68687 * q8 / 18874368;
|
|
34
|
+
return -a0;
|
|
35
|
+
} else {
|
|
36
|
+
const double q3 = std::pow(q, 3);
|
|
37
|
+
const double q5 = std::pow(q, 5);
|
|
38
|
+
const double q7 = std::pow(q, 7);
|
|
39
|
+
|
|
40
|
+
const double b1 = 1 - q - q2 / 8 + q3 / 64 + q4 / 1536 + 11 * q5 / 36864 + 49 * q6 / 589824 - 55 * q7 / 9437184 - 265 * q8 / 113246208;
|
|
41
|
+
|
|
42
|
+
return b1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// a value of the apex of the first stability region (a_peak ~ 0.237).
|
|
48
|
+
inline double get_first_stability_apex_a(const double q_peak = kFirstStabilityQPeak)
|
|
49
|
+
{
|
|
50
|
+
return get_first_stability_boundary(q_peak, q_peak);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
} // namespace qmfcalc
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
#endif
|