From 5a18e48c2dc36565916e325f2d9476b2bfbf663b Mon Sep 17 00:00:00 2001 From: Markus Rosenstihl Date: Tue, 20 Oct 2026 22:48:30 +0200 Subject: [PATCH] New functions: get_mean and get_phase, added better documentation --- AGENTS.md | 4 +- doc/api_reference.rst | 2 +- doc/manual.rst | 7 +- src/damaris/data/Accumulation.py | 119 +++++++++++++++++++++++++------ 4 files changed, 105 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 68548b2..55135cf 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,5 +16,5 @@ This is the front end part of GUI for controlling NMR spectrometers. On "Start", 10. The gitea workflow can be found at .gitea/workflows/debian-build.yml 11. Do not edit anything in "build", it is regenerated 12. Use sphinx for documentation, autodoc and autosummary is used for references of all classes, functions and modules. -13. If a new function/method is created document a short usage in doc/api_reference.rst and a longer explanation in doc/manual.rst -14. Do not use the Makefile in ./doc/, Doxygen is not used anymore. +13. If a new function/method is created document a short usage and an example in the doc string. Add a longer explanation in doc/manual.rst in the command list. Use Sphinx autodoc and autosummary grammar to describe it. Use "timesignal" as ADC_Result object and "accu" as Accumulation object. +14. Doxygen is not used anymore! diff --git a/doc/api_reference.rst b/doc/api_reference.rst index 169b8cc..8e3f24c 100644 --- a/doc/api_reference.rst +++ b/doc/api_reference.rst @@ -6,7 +6,7 @@ This page provides an auto-generated reference of all DAMARIS classes, functions It is built from docstrings using Sphinx autodoc and autosummary. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 :caption: Modules: damaris/data diff --git a/doc/manual.rst b/doc/manual.rst index a0f9679..c12ba35 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -458,9 +458,9 @@ Data Processing Outside DAMARIS For post-processing of the data, several options are available, depending on the format of the data. Most programs can handle ASCII files (gnuplot, Origin, fitsh, etc.), so this is the more flexible format. HDF can be read by pyTables, Matlab, Octave, R, Mathematica and more. - +************************** Experiment Script Commands -========================== +************************** Following is a list of available commands in an experiment script. @@ -663,8 +663,9 @@ Following are convenient functions for use in loops. for tp in combine_ranges(mylinrange, mylogrange): yield inversion_recovery(tau=t) +********************** Result Script Commands -====================== +********************** Following is a list of available commands in a result script. These are methods to be applied to a ADC_Result and Accumulation object. An accumulation object needs to be created before any methods can be applied. diff --git a/src/damaris/data/Accumulation.py b/src/damaris/data/Accumulation.py index dcf6f32..b10aadd 100644 --- a/src/damaris/data/Accumulation.py +++ b/src/damaris/data/Accumulation.py @@ -220,11 +220,49 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath): self.lock.release() return tmp_y + def get_ymean(self, start: float | None = None, stop: float | None = None) -> numpy.ndarray: + """ + return mean data from start to stop (in time units). Defaults to mean of whole channel data. + + Example: + accu = Accumulation(...) + means = accu.get_ymean(start=1e-6, stop=4e-6) + + :param start: start of the data + :param stop: stop of the data + :return: means for each channel + """ + if start == None and stop == None: + means = [ch.mean() for ch in self.y] + else: + istart = int(self.get_sampling_rate()*start) + istop = int(self.get_sampling_rate()*stop) + means = [ch[istart:istop].mean() for ch in self.y] + return numpy.array(means) + + def get_phase(self, pos: float | None = None) -> float: + """ + Return phase angle in degrees at a given time position. + + The phase is calculated from the first two channels using arctan2(y1, y0) + and converted to degrees. If pos is None, the phase at the first sample is returned. + + Example: + accu = Accumulation(...) + phase = accu.get_phase(pos=1e-6) + :param pos: time position in seconds + :return: phase angle in degrees + """ + if pos is None: + ipos = 0 + else: + ipos = int(self.get_sampling_rate()*pos) + return numpy.arctan2(self.y[1][ipos], self.y[0][ipos])*180/numpy.pi + def get_ymin(self) -> float|int: if not self.contains_data(): return 0 - tmp_min = [] self.lock.acquire() for i in range(self.get_number_of_channels()): @@ -257,12 +295,24 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath): return max(tmp_max) def get_job_id(self) -> int | None: + """ + Returns the job identifier associated with this accumulation. + """ return self.job_id # modified by Oleg Petrov def write_to_csv(self, destination: str | IO[str] = sys.stdout, delimiter: str = " ") -> None: """ - writes the data to a file. - destination can be a filehandle or a filename, default sys.stdout + Write the accumulation data to a whitespace-delimited text file with comment headers. + + The file starts with comment lines containing accumulation metadata and a header line + with channel names. Data rows contain time and channel means, optionally with errors. + + Example: + accu = Accumulation(...) + accu.write_to_csv('data.txt', delimiter=' ') + + :param destination: file handle or filename to write to. Defaults to sys.stdout. + :param delimiter: delimiter to use between columns. Defaults to " ". """ if isinstance(destination, str): with open(destination, "w") as f: @@ -305,9 +355,18 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath): # ------------- added by Oleg Petrov, 14 Feb 2012 ---------------------- def write_to_simpson(self, destination: str | IO[str] = sys.stdout, delimiter: str = " ", frequency: float = 100e6) -> None: """ - writes the data to a text file or sys.stdout in Simpson format, - for further processing with the NMRnotebook software; - destination can be a file or a filename + Write the accumulation data to a text file in Simpson format for NMRnotebook. + + The output contains a simple header with NP, SW, REF and TYPE=FID followed by + the data block. + + Example: + accu = Accumulation(...) + accu.write_to_simpson('data.simp', frequency=100e6) + + :param destination: file handle or filename. Defaults to sys.stdout. + :param delimiter: column delimiter for data rows. Defaults to space. + :param frequency: reference frequency. Defaults to 100e6. """ if isinstance(destination, str): with open(destination, "w") as f: @@ -337,14 +396,26 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath): # ------------- added by Oleg Petrov, 10 Sep 2013 ----------------------- def write_to_tecmag(self, destination = sys.stdout, nrecords: int = 1,\ - frequency: float = 100.,\ - last_delay: float = 1.,\ - receiver_phase: float = 0.,\ - nucleus: str = '1H') -> None: + frequency: float = 100.,\ + last_delay: float = 1.,\ + receiver_phase: float = 0.,\ + nucleus: str = '1H') -> None: """ - writes the data to a binary file in TecMag format; - destination can be a file object or a filename; - nrecords determines an indirect dimension in 2D experiments; + Write the accumulation data to a binary TecMag file. + + The file is written in TNT1.005 format compatible with TecMag processing. + Requires job_id and time_period to be set. + + Example: + accu = Accumulation(...) + accu.write_to_tecmag('data.tnt', nrecords=1, frequency=100.) + + :param destination: file handle or filename. + :param nrecords: number of indirect dimension records for 2D experiments. + :param frequency: spectrometer frequency. + :param last_delay: last delay value. + :param receiver_phase: receiver phase. + :param nucleus: nucleus label, defaults to '1H'. """ #TODO: Function is most likely broken in Python 3 because Strings are now unicode and binary files cannot write unicode @@ -467,15 +538,21 @@ class Accumulation(Errorable, Drawable, DamarisFFT, Signalpath): def write_to_hdf(self, hdffile: tables.File, where: str, name: str, title: str, complib: str | None = None, complevel: int | None = None) -> None: """ - Write the accumulation data to an HDF5 file. + Write the accumulation data to an HDF5 file using PyTables. - Parameters: - - hdffile: The HDF5 file object to write to. - - where: The location within the HDF5 file where the data should be stored. - - name: The name of the dataset within the HDF5 file. - - title: The title or description of the dataset. - - complib: Compression library to use for the dataset. - - complevel: Compression level for the dataset. + Creates a group with indices, job_ids and accu_data arrays. Metadata and + sampling rate are stored as attributes. + + Example: + accu = Accumulation(...) + accu.write_to_hdf(h5file, '/data', 'accu1', 'Accumulation') + + :param hdffile: PyTables File object to write to. + :param where: location within the HDF5 file. + :param name: name of the dataset group. + :param title: title/description of the dataset. + :param complib: compression library, e.g. 'zlib'. + :param complevel: compression level. """ accu_group=hdffile.create_group(where=where,name=name,title=title) accu_group._v_attrs.damaris_type="Accumulation"