Commit ccf82e5d authored by Samuel Rowlinson's avatar Samuel Rowlinson

Merge branch 'refactor/cython_directive_cleanup' into 'master'

Cleaning up Cython directives applied both globally and locally

Closes #259

See merge request !63
parents 3c65b66f 7561d903
......@@ -46,7 +46,7 @@ build/debug:
# Install editable mode so in-place pyx files are picked up by coverage.
- pip3 install -e .
# Build Cython extensions with coverage support.
- python3 setup.py build_ext --force --inplace --define CYTHON_TRACE --define CYTHON_TRACE_NOGIL
- python3 setup.py --coverage build_ext --force --inplace
# Make a platform-specific wheel.
- python3 setup.py bdist_wheel -d wheelhouse
artifacts:
......
default:
python3 setup.py build_ext --inplace
debug:
python3 setup.py --debug build_ext --inplace
# Build Cython extensions with the CYTHON_TRACE flag enabled to allow coverage tracking.
coverage:
python3 setup.py build_ext --force --inplace --define CYTHON_TRACE --define CYTHON_TRACE_NOGIL
python3 setup.py --coverage build_ext --force --inplace
clean:
find . -name "*.so" -type f -delete
......
......@@ -41,13 +41,36 @@ def make_extension(relpath, **kwargs):
return ".".join(names)
include_dirs = [finesse_dir, np.get_include()]
library_dirs = []
# The optional arguments consisting of various directories,
# macros, compilation args, etc. that will be passed to
# Extension object constructor
ext_args = {
"include_dirs": [],
"define_macros": [],
"undef_macros": [],
"library_dirs": [],
"libraries": [],
"runtime_library_dirs": [],
"extra_objects": [],
"extra_compile_args": [],
"extra_link_args": [],
"export_symbols": [],
"cython_include_dirs": [],
"cython_directives": [],
}
### Setting up some global options that need to be passed ###
### to all extensions ###
include_dirs = ext_args.get("include_dirs")
# Include the src/finesse and NumPy header file directories
include_dirs.extend([finesse_dir, np.get_include()])
# Now ensure suitesparse headers get included
USR_SUITESPARSE_PATH = "/usr/include/suitesparse"
if os.path.exists(USR_SUITESPARSE_PATH):
include_dirs.append(USR_SUITESPARSE_PATH)
# Grab the paths to suitesparse from conda if using this
conda_include, conda_lib = get_conda_paths()
if conda_include is not None:
CONDA_SUITESPARSE_PATH = os.path.join(conda_include, "suitesparse")
......@@ -55,68 +78,47 @@ def make_extension(relpath, **kwargs):
include_dirs.append(CONDA_SUITESPARSE_PATH)
include_dirs.append(conda_include)
library_dirs.append(conda_lib)
if "library_dirs" in kwargs:
ldirs = kwargs["library_dirs"]
if isinstance(ldirs, str):
ldirs = [ldirs]
library_dirs += ldirs
libraries = None
if "libraries" in kwargs:
libs = kwargs["libraries"]
if isinstance(libs, str):
libs = [libs]
ext_args.get("library_dirs").append(conda_lib)
libraries = libs
# Stops numpy version warning, cython uses an older API on purpose
define_macros = [
("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"),
# ("CYTHON_TRACE", "1"),
]
extra_compile_args = [
"-O3",
"-Wno-unused-function",
"-Wno-unused-variable",
# "-DCYTHON_WITHOUT_ASSERTIONS"
]
define_macros = ext_args.get("define_macros")
define_macros.extend(
[
# Stops numpy version warning, cython uses an older API on purpose
("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION"),
]
)
if "extra_compile_args" in kwargs:
eca = kwargs["extra_compile_args"]
if isinstance(eca, str):
eca = [eca]
extra_compile_args = ext_args.get("extra_compile_args")
extra_compile_args.extend(
[
"-O3",
# Inlined cpdef functions in finesse.cymath extensions complain
# about not being used (they are used outside of these extensions)
# so we suppress these warnings for the moment
"-Wno-unused-function",
"-Wno-unused-variable",
# "-DCYTHON_WITHOUT_ASSERTIONS"
]
)
extra_compile_args += eca
### Now adding the optional extra args needed for this specific extension ###
extra_link_args = None
if "extra_link_args" in kwargs:
ela = kwargs["extra_link_args"]
if isinstance(ela, str):
ela = [ela]
for arg_opt, arg_list in ext_args.items():
extra_args = kwargs.get(arg_opt)
if extra_args:
if isinstance(extra_args, str):
extra_args = [extra_args]
extra_link_args = ela
arg_list += extra_args
ext_name = "finesse." + construct_ext_name(relpath)
sources = [os.path.join(finesse_dir, relpath)]
return Extension(
ext_name,
sources,
include_dirs=include_dirs,
library_dirs=library_dirs,
libraries=libraries,
extra_compile_args=extra_compile_args,
extra_link_args=extra_link_args,
define_macros=define_macros,
language="c",
)
return Extension(ext_name, sources, language="c", **ext_args,)
def ext_modules():
# Argument pattern for extensions requiring OpenMP
if SYS_NAME == "Darwin":
open_mp_args = {
"extra_compile_args": ["-Xpreprocessor", "-fopenmp"],
......@@ -125,51 +127,68 @@ def ext_modules():
else:
open_mp_args = {"extra_compile_args": "-fopenmp", "extra_link_args": "-fopenmp"}
# Argument pattern for extensions using KLU
cmatrix_args = {"libraries": "klu"}
# The argument patterns that get passed to all extensions
default_ext_args = {}
COVERAGE_MODE = "--coverage" in sys.argv
if COVERAGE_MODE:
# If we're in coverage report mode, then add the trace
# macros to all extensions so that proper line tracing
# is performed
default_ext_args["define_macros"] = [
("CYTHON_TRACE", "1"),
("CYTHON_TRACE_NOGIL", "1"),
]
# NOTE (sjr) Pass any extra arguments that a specific extension needs via a
# dict of the arg names: values here. See ext_args in make_extension
# function above for the options.
ext_args = [
("cymath/complex.pyx", {}),
("cymath/math.pyx", {}),
("cymath/gaussbeam.pyx", {}),
("cymath/homs.pyx", {}),
("tree.pyx", {}),
("constants.pyx", {}),
("frequency.pyx", {}),
("symbols.pyx", {}),
("parameter.pyx", {}),
("cyexpr.pyx", {}),
("element.pyx", {}),
("cmatrix.pyx", cmatrix_args),
("knm/matrix.pyx", {}),
("knm/bayerhelms.pyx", open_mp_args),
("simulations/base.pyx", {}),
("simulations/basematrix.pyx", {}),
("simulations/KLU.pyx", {}),
("components/workspace.pyx", {}),
("components/mechanical.pyx", {}),
("components/modal/workspace.pyx", {}),
("components/modal/mirror.pyx", {}),
("components/modal/beamsplitter.pyx", {}),
("components/modal/signal.pyx", {}),
("components/modal/space.pyx", {}),
("components/modal/laser.pyx", {}),
("components/modal/lens.pyx", {}),
("components/modal/modulator.pyx", {}),
("components/modal/isolator.pyx", {}),
("components/modal/cavity.pyx", {}),
("components/modal/variable.pyx", {}),
("components/modal/squeezer.pyx", {}),
("detectors/workspace.pyx", {}),
("detectors/compute/amplitude.pyx", {}),
("detectors/compute/camera.pyx", open_mp_args),
("detectors/compute/power.pyx", open_mp_args),
("detectors/compute/quantum.pyx", {}),
("detectors/compute/gaussian.pyx", {}),
("tracing/ctracer.pyx", {}),
("tracing/tools.pyx", {}),
("analysis/runners.pyx", {}),
("solutions/base.pyx", {}),
("solutions/array.pyx", {}),
("cymath/complex.pyx", default_ext_args),
("cymath/math.pyx", default_ext_args),
("cymath/gaussbeam.pyx", default_ext_args),
("cymath/homs.pyx", default_ext_args),
("tree.pyx", default_ext_args),
("constants.pyx", default_ext_args),
("frequency.pyx", default_ext_args),
("symbols.pyx", default_ext_args),
("parameter.pyx", default_ext_args),
("cyexpr.pyx", default_ext_args),
("element.pyx", default_ext_args),
("cmatrix.pyx", {**default_ext_args, **cmatrix_args}),
("knm/matrix.pyx", default_ext_args),
("knm/bayerhelms.pyx", {**default_ext_args, **open_mp_args}),
("simulations/base.pyx", default_ext_args),
("simulations/basematrix.pyx", default_ext_args),
("simulations/KLU.pyx", default_ext_args),
("components/workspace.pyx", default_ext_args),
("components/mechanical.pyx", default_ext_args),
("components/modal/workspace.pyx", default_ext_args),
("components/modal/mirror.pyx", default_ext_args),
("components/modal/beamsplitter.pyx", default_ext_args),
("components/modal/signal.pyx", default_ext_args),
("components/modal/space.pyx", default_ext_args),
("components/modal/laser.pyx", default_ext_args),
("components/modal/lens.pyx", default_ext_args),
("components/modal/modulator.pyx", default_ext_args),
("components/modal/isolator.pyx", default_ext_args),
("components/modal/cavity.pyx", default_ext_args),
("components/modal/variable.pyx", default_ext_args),
("components/modal/squeezer.pyx", default_ext_args),
("detectors/workspace.pyx", default_ext_args),
("detectors/compute/amplitude.pyx", default_ext_args),
("detectors/compute/camera.pyx", {**default_ext_args, **open_mp_args}),
("detectors/compute/power.pyx", {**default_ext_args, **open_mp_args}),
("detectors/compute/quantum.pyx", default_ext_args),
("detectors/compute/gaussian.pyx", default_ext_args),
("tracing/ctracer.pyx", default_ext_args),
("tracing/tools.pyx", default_ext_args),
("analysis/runners.pyx", default_ext_args),
("solutions/base.pyx", default_ext_args),
("solutions/array.pyx", default_ext_args),
]
exts = []
......@@ -180,23 +199,37 @@ def ext_modules():
num_jobs = 0
else:
num_jobs = os.cpu_count()
#
# from Cython.Compiler.Options import directive_defaults
#
# directive_defaults['linetrace'] = True
# directive_defaults['binding'] = True
# annotate flag produces html files showing level of python interaction in each cython file
# See https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#compiler-directives
# for in-depth details on the options for compiler directives
compiler_directives = {
# Embeds call signature in docstring of Python visible functions
"embedsignature": True,
# No checks are performed on division by zero (for big perfomance boost)
"cdivision": True,
}
# If coverage mode is set the ensure line tracing
# is switched on for all extensions
if COVERAGE_MODE:
compiler_directives["linetrace"] = True
sys.argv.remove("--coverage")
# If debug mode is set then ensure profiling
# is switched on for all extensions
DEBUG_MODE = "--debug" in sys.argv
if DEBUG_MODE:
compiler_directives["profile"] = True
sys.argv.remove("--debug")
return cythonize(
exts,
# Produces HTML files showing level of CPython interaction
# per-line of each Cython extension (.pyx) file
annotate=True,
language_level=3,
nthreads=num_jobs,
compiler_directives={
"embedsignature": True,
"cdivision": True,
"linetrace": True,
},
compiler_directives=compiler_directives,
gdb_debug=True,
)
......@@ -301,8 +334,6 @@ setup(
package_data={
"finesse": [
"plotting/style/*.mplstyle",
"cmath/*.pxd",
"simulations/*.pxd",
"finesse.ini", # Base config file.
"usr.ini.dist", # Barebone user config file.
]
......
......@@ -96,7 +96,6 @@ cdef class MIMOTFWorkspace(ConnectorWorkspace):
@cython.wraparound(False)
@cython.boundscheck(False)
@cython.initializedcheck(False)
@cython.cdivision(True)
cdef inline complex_t eval_tf_term(complex_t s, double* coeffs, int N):
cdef:
int i
......@@ -110,7 +109,6 @@ cdef inline complex_t eval_tf_term(complex_t s, double* coeffs, int N):
@cython.wraparound(False)
@cython.boundscheck(False)
@cython.initializedcheck(False)
@cython.cdivision(True)
cdef inline complex_t eval_tf(complex_t s, double* num, int N, complex_t den):
return eval_tf_term(s, num, N)/den
......@@ -118,7 +116,6 @@ cdef inline complex_t eval_tf(complex_t s, double* num, int N, complex_t den):
@cython.wraparound(False)
@cython.boundscheck(False)
@cython.initializedcheck(False)
@cython.cdivision(True)
cpdef eval_tf_vec(const complex_t[::1] s, const double[::1] num, const double[::1] den, complex_t[::1] out):
cdef:
int i
......@@ -315,13 +312,13 @@ class FreeMass(Connector):
ws.owner_id, ws.signal.connections.F_to_Z_idx, 0, 0,
) as mat:
mat[:] = -1 / (ws.values.mass * (2*PI*f)**2)
if ws.signal.connections.F_to_YAW_idx >= 0:
with ws.sim.signal.component_edge_fill3(
ws.owner_id, ws.signal.connections.F_to_YAW_idx, 0, 0,
) as mat:
mat[:] = -1 / (ws.values.I_yaw * (2*PI*f)**2)
if ws.signal.connections.F_to_PITCH_idx >= 0:
with ws.sim.signal.component_edge_fill3(
ws.owner_id, ws.signal.connections.F_to_PITCH_idx, 0, 0,
......@@ -407,17 +404,17 @@ class Pendulum(Connector):
) as mat:
omega0 = 2 * PI * ws.values.fz
mat[:] = 1 / ws.values.mass * 1/(s**2 + s * omega0/ws.values.Qz + omega0**2)
if ws.signal.connections.F_to_YAW_idx >= 0:
with ws.sim.signal.component_edge_fill3(
ws.owner_id, ws.signal.connections.F_to_YAW_idx, 0, 0,
) as mat:
omega0 = 2 * PI * ws.values.fyaw
mat[:] = 1 / ws.values.I_yaw * 1/(s**2 + s * omega0/ws.values.Qyaw + omega0**2)
if ws.signal.connections.F_to_PITCH_idx >= 0:
with ws.sim.signal.component_edge_fill3(
ws.owner_id, ws.signal.connections.F_to_PITCH_idx, 0, 0,
) as mat:
omega0 = 2 * PI * ws.values.fpitch
mat[:] = 1 / ws.values.I_pitch * 1/(s**2 + s * omega0/ws.values.Qpitch + omega0**2)
\ No newline at end of file
mat[:] = 1 / ws.values.I_pitch * 1/(s**2 + s * omega0/ws.values.Qpitch + omega0**2)
#cython: profile=True, boundscheck=False, wraparound=False, initializedcheck=False
#cython: boundscheck=False, wraparound=False, initializedcheck=False
cimport numpy as np
import numpy as np
......@@ -61,7 +61,7 @@ cdef class BeamsplitterSignalConnections(BeamsplitterOpticalConnections):
int Nfo = mtx.optical_frequencies.size
Nmz = bs.mech.z.num_frequencies # num of mechanic frequencies
self.P1i_Fz = SubCCSView2DArray(Nfo, Nmz)
self.P1o_Fz = SubCCSView2DArray(Nfo, Nmz)
self.P2i_Fz = SubCCSView2DArray(Nfo, Nmz)
......@@ -297,9 +297,6 @@ cdef class BeamsplitterWorkspace(KnmConnectorWorkspace):
ch_sym,
)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef update_parameter_values(self):
ConnectorWorkspace.update_parameter_values(self)
......@@ -506,8 +503,8 @@ cdef object c_beamsplitter_signal_fill(ConnectorWorkspace cws):
cdef void get_carrier_vectors(BeamsplitterWorkspace ws, int carrier_index,
DenseZVector *c_p1_i, DenseZVector *c_p2_i,
DenseZVector *c_p3_i, DenseZVector *c_p4_i,
DenseZVector *c_p1_i, DenseZVector *c_p2_i,
DenseZVector *c_p3_i, DenseZVector *c_p4_i,
DenseZVector *c_p1_o, DenseZVector *c_p2_o,
DenseZVector *c_p3_o, DenseZVector *c_p4_o
):
......@@ -577,7 +574,7 @@ cdef void multiple_z_mechanical_freq_signal_calc (
single_z_mechanical_frequency_signal_calc(
ws, conn, car_conn, freq, phi, j, i
)
cdef void single_z_mechanical_frequency_signal_calc (
BeamsplitterWorkspace ws,
......@@ -751,7 +748,7 @@ cdef void single_yaw_mechanical_frequency_signal_calc (
-0.5 * (ws.nr1 - ws.nr2) * wx1o * a_2_o_factor,
ws.K_yaw_sig.data_view, True
)
if conn.yaw_P2o[yaw_freq_idx][freq.index]:
wx2o = beam_size(q_P2o.qx, ws.nr1, ws.sim.model_data.lambda0)
# fill_prop_za as off-diagonal -1 is already included in the carrier connection
......@@ -826,7 +823,7 @@ cdef void single_yaw_mechanical_frequency_signal_calc (
(<SubCCSView>conn.P2o_Fyaw[freq.index][yaw_freq_idx]).fill_negative_za_zmvc (
-ws.nr1 * wx2o * ws.field1_to_F, &ws.K_yaw_sig.mtx, &c_p2_o
)
if conn.P3i_Fyaw[freq.index][yaw_freq_idx]:
wx3i = beam_size(q_P3i.qx, ws.nr2, ws.sim.model_data.lambda0)
(<SubCCSView>conn.P3i_Fyaw[freq.index][yaw_freq_idx]).fill_negative_za_zmvc (
......@@ -906,7 +903,7 @@ cdef void single_pitch_mechanical_frequency_signal_calc (
-0.5 * (ws.nr1 - ws.nr2) * wy1o * a_2_o_factor,
ws.K_pitch_sig.data_view, True
)
if conn.pitch_P2o[pitch_freq_idx][freq.index]:
wy2o = beam_size(q_P2o.qy, ws.nr1, ws.sim.model_data.lambda0)
# fill_prop_za as off-diagonal -1 is already included in the carrier connection
......@@ -981,7 +978,7 @@ cdef void single_pitch_mechanical_frequency_signal_calc (
(<SubCCSView>conn.P2o_Fpitch[freq.index][pitch_freq_idx]).fill_negative_za_zmvc (
-ws.nr1 * wy2o * ws.field1_to_F, &ws.K_pitch_sig.mtx, &c_p2_o
)
if conn.P3i_Fpitch[freq.index][pitch_freq_idx]:
wy3i = beam_size(q_P3i.qy, ws.nr2, ws.sim.model_data.lambda0)
(<SubCCSView>conn.P3i_Fpitch[freq.index][pitch_freq_idx]).fill_negative_za_zmvc (
......@@ -1000,4 +997,4 @@ cdef void single_pitch_mechanical_frequency_signal_calc (
if conn.P4o_Fpitch[freq.index][pitch_freq_idx]:
(<SubCCSView>conn.P4o_Fpitch[freq.index][pitch_freq_idx]).fill_negative_za_zmvc (
ws.nr2 * wy4o * ws.field2_to_F, &ws.K_pitch_sig.mtx, &c_p4_o
)
\ No newline at end of file
)
#cython: profile=True, boundscheck=False, wraparound=False, initializedcheck=False
#cython: boundscheck=False, wraparound=False, initializedcheck=False
from finesse.cymath cimport complex_t
from finesse.simulations.base cimport NodeBeamParam
......
#cython: profile=True, boundscheck=False, wraparound=False, initializedcheck=False
#cython: boundscheck=False, wraparound=False, initializedcheck=False
cimport cython
......@@ -83,9 +83,6 @@ cdef class LensWorkspace(KnmConnectorWorkspace):
self.sym_abcd_Cy = cy_expr_new()
cy_expr_init(self.sym_abcd_Cy, ch_sym)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef update_parameter_values(self):
ConnectorWorkspace.update_parameter_values(self)
......
#cython: profile=True, boundscheck=False, wraparound=False, initializedcheck=False
#cython: boundscheck=False, wraparound=False, initializedcheck=False
cimport cython
......@@ -215,9 +215,6 @@ cdef class MirrorWorkspace(KnmConnectorWorkspace):
self.sym_abcd_Cs[i] = cy_expr_new()
cy_expr_init(self.sym_abcd_Cs[i], ch_sym)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cpdef update_parameter_values(self):
ConnectorWorkspace.update_parameter_values(self)
......
# distutils: sources = src/finesse/tinyexpr.c
# distutils: include_dirs = src/finesse/
# cython: profile=True
"""Compiled symbolic expressions used internally via parameters and element workspaces.
......
#cython: boundscheck=False, wraparound=False, initializedcheck=False, profile=True
#cython: boundscheck=False, wraparound=False, initializedcheck=False
from finesse.cymath cimport complex_t
from finesse.cymath.complex cimport conj, cexp, carg
......@@ -99,7 +99,7 @@ cdef class ADWorkspace(MaskedDetectorWorkspace):
self.set_output_fn(ad_multi_field_output_masked)
else:
self.set_output_fn(ad_multi_field_output)
if owner.node.type == NodeType.OPTICAL:
self.scaling = sqrt(0.5 * self.sim.model_data.EPSILON0_C)
elif owner.node.type == NodeType.MECHANICAL:
......
#cython: boundscheck=False, wraparound=False, initializedcheck=False, profile=True
#cython: boundscheck=False, wraparound=False, initializedcheck=False
cimport numpy as np
import numpy as np
......@@ -164,9 +164,6 @@ cdef class PD1Workspace(MaskedDetectorWorkspace):
pd1_AC_output = OutputFuncWrapper.make_from_ptr(c_pd1_AC_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_pd1_AC_output(DetectorWorkspace dws):
"""This expects a `PD1Workspace` input"""
cdef:
......@@ -196,9 +193,6 @@ cdef object c_pd1_AC_output(DetectorWorkspace dws):
pd1_DC_output = OutputFuncWrapper.make_from_ptr(c_pd1_DC_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_pd1_DC_output(DetectorWorkspace dws):
"""This expects a `PD1Workspace` input"""
cdef:
......@@ -258,9 +252,6 @@ cdef class PD2Workspace(MaskedDetectorWorkspace):
self.v = <PD2Values>self.values
pd2_DC_output = OutputFuncWrapper.make_from_ptr(c_pd2_DC_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_pd2_DC_output(DetectorWorkspace dws):
"""This expects a `PD2Workspace` input"""
cdef:
......@@ -311,9 +302,6 @@ cdef object c_pd2_DC_output(DetectorWorkspace dws):
pd2_AC_output = OutputFuncWrapper.make_from_ptr(c_pd2_AC_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_pd2_AC_output(DetectorWorkspace dws):
"""This expects a `PD2Workspace` input"""
cdef:
......
#cython: boundscheck=False, wraparound=False, initializedcheck=False, profile=True
#cython: boundscheck=False, wraparound=False, initializedcheck=False
cimport numpy as np
import numpy as np
......@@ -110,9 +110,6 @@ cdef class QND0Workspace(DetectorWorkspace):
self.cov = v
qnd0_output = OutputFuncWrapper.make_from_ptr(c_qnd0_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_qnd0_output(DetectorWorkspace self):
cdef:
QND0Workspace ws = <QND0Workspace> self
......@@ -314,9 +311,6 @@ cdef class QNDNWorkspace(DetectorWorkspace):
qndN_output = OutputFuncWrapper.make_from_ptr(c_qndN_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_qndN_output(DetectorWorkspace self):
cdef:
QNDNWorkspace ws = <QNDNWorkspace> self
......@@ -439,9 +433,6 @@ cdef class QShot0Workspace(DetectorWorkspace):
qshot0_output = OutputFuncWrapper.make_from_ptr(c_qshot0_output)
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.initializedcheck(False)
cdef object c_qshot0_output(DetectorWorkspace self):
cdef