Metadata-Version: 2.1
Name: fepydas
Version: 0.0.85
Summary: felix' python data analysis suite
Home-page: https://git.tu-berlin.de/nippert/fepydas
Author: Felix Nippert
Author-email: felix@physik.tu-berlin.de
Project-URL: Bug Tracker, https://git.tu-berlin.de/nippert/fepydas/issues
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.6
Description-Content-Type: text/markdown
Requires-Dist: numpy >=2.0.0
Requires-Dist: scipy
Requires-Dist: lmfit
Requires-Dist: matplotlib
Requires-Dist: scikit-learn
Requires-Dist: pyamg
Requires-Dist: sympy >=1.13.3
Requires-Dist: pathos
Requires-Dist: tqdm
Requires-Dist: ptufile

# fepydas

**fepydas** ("felix' python data analysis suite") is a Python 3 library for analysing and
fitting optical-spectroscopy and photophysics data: photoluminescence (PL), PL excitation
(PLE), Raman, time-resolved PL (TCSPC decays), external quantum efficiency (EQE) and
spatially-resolved spectrum maps.

It gives you a small, unit-aware data model plus ready-made readers, fitters and plotters, so a
typical analysis is just: **load a file → manipulate the dataset → fit → plot/export.**

```python
from fepydas.constructors.datareaders.Custom import ASCII_Spectrum
from fepydas.workers.Fit import GaussianFit

spectrum = ASCII_Spectrum("measurement.txt")   # -> a Spectrum
spectrum.cutAxis(500, 600)                      # keep 500-600 nm
spectrum.normalize()                            # scale peak to 1

fit = GaussianFit()
fit.initializeAutoFromSpectrum(spectrum)        # guess starting parameters
fit.fitSpectrum(spectrum)                        # run the fit

spectrum.toPlot().save("spectrum.png")          # save a plot
spectrum.export("spectrum.txt")                 # save tab-separated data
```

- **Install:** `pip install fepydas` (from [PyPI](https://pypi.org/project/fepydas/))
- **Repository:** <https://git.tu-berlin.de/nippert/fepydas>
- **Runnable examples:** [`examples/`](https://git.tu-berlin.de/nippert/fepydas/-/tree/master/examples)

---

## Table of contents

1. [Installation](#installation)
2. [Core concepts](#core-concepts)
3. [Loading data](#loading-data)
4. [Working with spectra and series](#working-with-spectra-and-series)
5. [Units, axes and calibration](#units-axes-and-calibration)
6. [Fitting](#fitting)
7. [Time-resolved decays (IRF convolution)](#time-resolved-decays-irf-convolution)
8. [Spectrum maps](#spectrum-maps)
9. [PLE measurements](#ple-measurements)
10. [Decomposition and clustering](#decomposition-and-clustering)
11. [Plotting](#plotting)
12. [Exporting and saving](#exporting-and-saving)
13. [Batch processing whole directories](#batch-processing-whole-directories)
14. [Worked examples](#worked-examples)
15. [API map](#api-map)
16. [Requirements](#requirements)

---

## Installation

```bash
pip install fepydas
```

Python 3 is required (3.9+ recommended, since `numpy >= 2.0` is a dependency). All other
dependencies (scipy, matplotlib, lmfit, scikit-learn, pyamg, sympy, pathos, tqdm, ptufile) are
installed automatically. See [Requirements](#requirements).

To work from a checkout instead:

```bash
git clone https://git.tu-berlin.de/nippert/fepydas
cd fepydas
pip install -e .
```

---

## Core concepts

fepydas is organised in three layers. You usually only touch the top two.

| Layer | Package | What it does |
|-------|---------|--------------|
| **Constructors** | `fepydas.constructors` | Read files into datasets, and build plots. |
| **Workers** | `fepydas.workers` | Fit, write ASCII, batch-load directories. |
| **Data model** | `fepydas.datatypes` | The unit-aware containers everything is built on. |

### The data model

- **`Data` / `Data1D` / `Data2D` / `Data3D`** — a numpy array (`.values`) plus a `DataType`
  (`.datatype`) and optional `.errors`. The dimensionality matches the structure: a single
  spectrum is `Data1D`, a stack of spectra is `Data2D`, a spatial cube is `Data3D`.
- **`DataType`** — a quantity name + a `Unit` (e.g. *"Wavelength"* in nm). Predefined ones live
  in `fepydas.datatypes` (`WL_nm`, `E_eV`, `COUNTS`, `TIME_ns`, `TEMP_K`, ...).
- **`Unit`** — wraps a sympy unit so values can be compared and converted (including the special
  wavelength ↔ energy case, with the Jacobian applied to intensities).

### Datasets

A **`Dataset`** pairs an x-axis with data. The hierarchy specialises this:

```
Dataset
├── Spectrum                       one spectrum (axis + Data1D)
└── SpectrumSeries                 many spectra sharing an axis, indexed by a "key" axis
    ├── BinnedSpectrumSeries       a series rebinned along its key axis
    ├── JoinSpectrumSeries         several separate spectra stacked into a series
    ├── MergeSpectraSeries         several series concatenated
    └── SpectrumSeriesWithCalibration
        ├── PLE                    emission spectra keyed by excitation wavelength
        └── BaseSpectrumMap
            ├── SpectrumMap        dense rectangular map (spectrum at every pixel)
            └── SparseSpectrumMap  map where only some pixels were measured

Spectrum
└── SpectrumWithIRF                a decay histogram bundled with its instrument response
    └── SpectraWithCommonIRF       several decays sharing one IRF

Map                                a single scalar value per map pixel (e.g. a fit result)
```

Most dataset methods **mutate in place** and return `None` (e.g. `cutAxis`, `normalize`,
`convertToEnergy`). Methods that **derive** a new object return it (e.g. `integrate`, `average`,
`collapse`, `toPlot`).

### The typical workflow

```python
ds = SomeReader(path)            # constructors.datareaders.* -> a dataset
ds.cutAxis(...); ds.normalize()  # in-place dataset operations
fit = SomeFit(); fit.initializeAutoFromSpectrum(ds); fit.fitSpectrum(ds)
ds.toPlot().save("plot.png")     # constructors.Plots
ds.export("data.txt")            # workers.ASCIIWriter
```

---

## Loading data

All readers are plain functions that return a dataset. Pick the one that matches your file.

### Generic / in-memory (`fepydas.constructors.datareaders.Generic`)

```python
from fepydas.constructors.datareaders.Generic import (
    GenericXY, GenericXMultiY, ASCII, Binary,
)
import numpy

# Build a Spectrum from arrays you already have. Units are abbreviation strings.
x = numpy.linspace(400, 700, 300)
y = numpy.exp(-((x - 550) ** 2) / 50)
spectrum = GenericXY(x, "Wavelength", "nm", y, "Counts", "")

# Build a SpectrumSeries from a shared axis and a 2D stack of y-data.
series = GenericXMultiY(x, "Wavelength", "nm", y2d, "Counts", "",
                        keys, "Temperature", "K")

# Read a raw numeric table (thin numpy.genfromtxt wrapper).
table = ASCII("data.txt", delimiter="\t", skip_header=0)

# Reload anything previously saved with .saveBinary()
obj = Binary("dataset.bin")
```

### TCSPC histograms (`Generic` + `Proprietary`)

```python
from fepydas.constructors.datareaders.Generic import Histogram, HistogramWithIRF

decay = Histogram("decay.phu")                      # .phu/.thi/.sdt/.ptu -> Spectrum
pair  = HistogramWithIRF("decay.sdt", "irf.sdt")    # -> SpectrumWithIRF
```

`Histogram` auto-detects the format from the extension:
`.phu` (PicoHarp), `.thi` (TimeHarp), `.sdt` (Becker & Hickl), `.ptu` (PicoQuant TTTR).
For large `.ptu` files the reader is chunked/low-RAM and takes options (forwarded through
`**kwargs`): `channel`, `bin_width_s`, `all_channels`, `number_of_bins`, `use_memmap`,
`show_progress`, `progress_callback`, ... See the `Histogram`/`PTUHistogram` docstrings.

### Lab-specific ASCII formats (`fepydas.constructors.datareaders.Custom`)

| Function | Returns | Use for |
|----------|---------|---------|
| `ASCII_Spectrum(file)` | `Spectrum` | a single spectrum (wavelength/counts) |
| `ASCII_TimeSeries(file)` | `SpectrumSeries` | spectra over time |
| `ASCII_RamanSeries(folder)` | `SpectrumSeries` | one Raman spectrum per file in a folder |
| `ASCII_Gelbes(file)` / `ASCII_TempSeries_Gelbes(file)` / `ASCII_TimeSeries_Gelbes(file)` | `Spectrum` / `SpectrumSeries` | the "Gelbes" lab format |
| `ASCII_SSTRPL(file)` | `SpectrumSeries` | steady-state-transient PL |
| `ASCII_EQE(file)` | `SpectrumSeries` | EQE current series (background-subtracted) |
| `ASCII_PLE(file, cal=None, resp=None)` | `PLE` | PLE map with optional calibration/response |
| `Mapscan(file)` / `Mapscan2(file)` | `SpectrumMap` | binary spectrum maps (float64 / uint16) |

### LabVIEW exports (`fepydas.constructors.datareaders.FromLabview`)

```python
from fepydas.constructors.datareaders.FromLabview import Automatic

ds = Automatic("measurement.plm")   # inspects the header and returns the right type
```

`Automatic` detects the measurement type from the file header and returns a `Spectrum`,
`SpectrumSeries`, `PLE`, `SparseSpectrumMap` or `Map` (dark-frame correction and tracked power
are applied automatically when present).

---

## Working with spectra and series

These operations are available on `Spectrum` and (where it makes sense) `SpectrumSeries`. They
mutate the dataset in place unless noted.

```python
# --- cropping ---
spectrum.cutAxis(500, 600)          # keep an axis value range
spectrum.cutAxisByIdx(50, 3000)     # keep an axis index range
series.cutKeys(10, 300)             # keep a key range (series only)

# --- intensity operations ---
spectrum.normalize()                # scale max to 1
series.normalize(individual=True)   # scale each spectrum to its own max
series.subtractBaseline()           # remove a flat baseline (no dark spectrum available)
spectrum.antiBloom(45000, width=2)  # mask CCD blooming above a threshold
spectrum.divideBySpectrum(reference)

# --- reductions (these RETURN new datasets) ---
integral = series.integrate()       # integrated intensity vs key  -> Spectrum
avg      = series.average()         # mean spectrum                -> Spectrum
total    = series.collapse()        # summed spectrum              -> Spectrum
peakpos  = series.maximum()         # peak axis position vs key    -> Spectrum

# --- selecting / combining ---
series.filterByIntegral(0.5)        # drop weak spectra (< 50% of the strongest integral)
hdr = series.highDynamicRange()     # merge differently-exposed spectra -> Spectrum
```

`identifyPeaks(threshold=10, width=10)` returns index ranges around detected peaks (used by the
calibration helpers).

---

## Units, axes and calibration

### Predefined data types

Import from `fepydas.datatypes`: `WL_nm`, `RS_cm` (Raman shift), `E_eV`, `COUNTS`, `NUMBER`,
`TIME_s`, `TIME_ns`, `TIME_ps`, `TEMP_K`, `ANGLE_au`, `I_mA`, `P_mW`, `POS_um`, `POS_mm`. Build
your own with `generateDataType("Power", "mW")` (the unit string is parsed by `unitFromAbbrev`).

### Wavelength ↔ energy

```python
spectrum.convertToEnergy("eV")   # converts the axis AND applies the Jacobian to intensities
```

This works on `Spectrum`, `SpectrumSeries` and the maps. At the `Data1D` level you can also call
`axis.transformTo(E_eV)` for any compatible unit.

### Vacuum correction

```python
spectrum.applyVacuumCorrection(temperature=20, pressure=101325, humidity=0, co2=610)
```

Converts air wavelengths to vacuum wavelengths using the refractive index of moist air.

### Spectral calibration

If a calibration spectrum (e.g. a lamp with known lines) is attached (`PLE`, the map types, or
anything that is a `SpectrumSeriesWithCalibration`):

```python
ds.calibrate(references=[None, 435.8335, 404.6565])  # known positions for detected peaks
```

Standalone, you can fit a calibration curve from measured vs reference line positions:

```python
from fepydas.workers.Fit import Calibration

cal = Calibration(measured_positions, nist_positions)   # fits on construction; saves a plot
calibrated_axis = cal.apply(other_axis.values)
```

---

## Fitting

Every fit model is a `Fit` subclass. The pattern is always: **create → initialise parameters →
fit → use the result.**

```python
from fepydas.workers.Fit import GaussianFit

fit = GaussianFit()
fit.initializeAutoFromSpectrum(spectrum)   # guess bg, I, x0, fwhm from the data
# (optional) constrain parameters before fitting:
fit.parameters["x0"].min = 419
fit.parameters["x0"].max = 421
fit.fitSpectrum(spectrum)                   # or fit.fit(x, y)

print(fit.result.params)                    # lmfit result
model_curve = fit.evaluate(spectrum.axis.values)
```

### Available models

| Class | Function fitted | Key parameters |
|-------|-----------------|----------------|
| `GaussianFit`, `LimitedGaussianFit` | Gaussian peak | `bg, I, x0, fwhm` |
| `LorentzianFit`, `SpectralLine` | Lorentzian peak | `bg, I, x0, fwhm` |
| `LinearFit`, `CalibrationFit` | `a*x + b` | `a, b` |
| `QuadraticFit`, `QuadraticCalibrationFit` | `a*x² + b*x + c` | `a, b, c` |
| `AutomaticCalibration` | quadratic axis calibration | (fits peaks → references on construction) |
| `ExponentialFit` | single exponential decay | `bg, I, x0, tau, rise` |
| `BiExponentialFit`, `TriExponentialFit` | 2-/3-component decay | `I_n, tau_n, ...` |
| `BiExponentialTailFit` | 2-component tail (no rise) | `bg, I_1, tau_1, I_2, tau_2` |

You can set parameters manually instead of `initializeAuto*`:

```python
from fepydas.workers.Fit import Fit
from lmfit import Parameters

def model(x, a=1, b=0):
    return a * x + b

fit = Fit(model)
p = Parameters()
p.add("a", value=1, min=0)
p.add("b", value=0)
fit.initializeParameters(p)
fit.fitSpectrum(spectrum)
```

### Fitting a whole series in parallel

```python
fit = GaussianFit()
fit.initializeAutoFromSpectrum(series.average())
series.fit(fit)                               # fits every spectrum (uses a process pool)
```

> **Windows note:** parallel fitting forks a process pool. On Windows, guard your script with
> `if __name__ == "__main__":` so child processes don't re-run the whole module.

### Saving fit reports

```python
fit.saveReport("fit.txt")          # parameter table + statistics
fit.saveLMfitReport("lmfit.txt")   # the raw lmfit fit_report
```

### `FitSimple` — fitting with native lmfit models

For workflows built directly on `lmfit.Model`, `FitSimple` offers parameter guessing, optional
parallel fitting and dynamic bound propagation:

```python
from fepydas.workers.Fit import FitSimple
from lmfit.models import GaussianModel

fs = FitSimple(GaussianModel(), spectrum_or_series)
fs.initguess()                         # or fs.setinitparams(params)
fs.fit(multiprocessing=True)           # y_err=..., bounds_tolerance=..., cores=... all supported
```

---

## Time-resolved decays (IRF convolution)

For TCSPC lifetime fitting, load the decay together with its instrument response function and
fit a model **convolved with the IRF**:

```python
from fepydas.constructors.datareaders.Generic import HistogramWithIRF
from fepydas.constructors.Plots import SpectrumWithIRFPlot
from fepydas.workers.Fit import ExponentialFit

s = HistogramWithIRF("Signal.sdt", "InstrumentResponseFunction.sdt")
s.normalize()

fit = ExponentialFit()
fit.initializeAutoFromSpectrum(s)
fit.parameters["rise"].value = 0.01
fit.parameters["rise"].vary = False
fit.convolutedFit(s.axis.values, s.data.values, s.IRF.values)

plot = SpectrumWithIRFPlot(s)
plot.addLine(fit.evaluateConvolution(s.axis.values, s.IRF.values), label="Convolution Fit")
plot.setYLog(); plot.legend()
plot.save("ConvolutionFit.png")
```

---

## Spectrum maps

Maps store a spectrum at every spatial pixel. `SpectrumMap` is a dense rectangular grid;
`SparseSpectrumMap` (e.g. from a masked LabVIEW scan) stores only measured pixels.

```python
from fepydas.constructors.datareaders.Custom import Mapscan
from fepydas.workers.Fit import GaussianFit
from fepydas.datatypes.Data import DataType

if __name__ == "__main__":           # required for parallel fitting on Windows
    m = Mapscan("Example.map")
    m.cutAxis(290, 360)              # restrict the spectral window

    m.integrate().toPlot().save("integrated.png")   # integrated intensity image
    m.maximum().toPlot().save("peakpos.png")        # peak-position image
    m.average().toPlot().save("avg_spectrum.png")   # mean spectrum

    # fit every pixel and turn a fit parameter into a map
    fit = GaussianFit()
    fit.initializeAutoFromSpectrum(m.average())
    m.fit(fit)
    posMap = m.fitParameterToMap(fit, "x0", DataType("Position", "nm"))
    posMap.toPlot(zRange=[330, 350]).save("fitted_position.png")
```

Maps also support clustering-based segmentation:

```python
clusters = m.fitMixture(n=2, nmf=3)               # spectral clustering (optionally NMF-reduced)
m.getClusterMapIdx(clusters).toPlot().save("clusters.png")
m.getClusterIntegratedSpectra(clusters).toPlot().save("cluster_spectra.png")
```

`integrate()`, `maximum()` and `fitParameterToMap(...)` return `Map` objects (one value per
pixel), which plot as false-color images via `toPlot(log=..., zRange=...)`.

---

## PLE measurements

`PLE` is a series of emission spectra keyed by excitation wavelength, with extra correction
steps. A full PLE workflow (from the [PLE example](examples/PLE/PLE.py)):

```python
from fepydas.constructors.datareaders.Custom import ASCII_PLE

ple = ASCII_PLE("PLE.txt", "Calibration.cal", "IntensityRef.dat")
ple.toPlot().save("01_raw.png")

ple.calibrate([None, 435.8335, 404.6565])     # calibrate the detection axis
ple.applyVacuumCorrection()
ple.subtractBaseline()
ple.applyLampResponseCorrection()              # divide by the lamp response
ple.calibrateLamp()                            # calibrate the excitation axis from the lamp line
ple.export("CalibratedPLE.txt")

ple.integrate().toPlot().save("integrated.png")
ple.normalize(individual=True)
ple.toPlot().save("normalized.png")            # false-color contour (log by default)
```

---

## Decomposition and clustering

`SpectrumSeries` (and maps) can be decomposed into spectral components using scikit-learn
(NMF / PCA / LDA) — useful for separating overlapping signals or removing a background.

```python
# find / inspect components
decomp, components = series.getDominatingComponents(algo="LDA", n=3)
components.toPlot().save("components.png")
strongest = series.getDominatingComponent(algo="PCA")     # -> Spectrum

# remove a background learned from a reference, then apply it to the signal
nmf = reference.removeDominatingComponents(n=10)   # fit + subtract, returns the decomposition
signal.removeComponents(nmf)                        # subtract the same components

# project each series onto a fitted decomposition
weights = series.getProjection(decomp)              # -> SpectrumSeries of component weights
```

---

## Plotting

Datasets expose `toPlot()` (and sometimes `toScatterPlot()`), which return a plot object. Call
`.save(path)` to write an image, `.show()` to open a window, or styling methods first.

```python
plot = series.toPlot()           # SpectrumSeriesPlot (overlaid, colored by key)
plot.setYLog()                   # log y-axis
plot.setXRange(400, 700)         # zoom x
plot.addLine(extra.data.values, "extra")   # overlay another curve
plot.legend()
plot.save("series.png", dpi=300, bbox_inches="tight")   # kwargs go to matplotlib savefig
```

| Dataset | `toPlot()` returns | Notes |
|---------|--------------------|-------|
| `Spectrum` | `SpectrumPlot` (line) / `SpectrumScatterPlot` | `toScatterPlot()` for points |
| `SpectrumSeries` | `SpectrumSeriesPlot` | `toPlot(polar=True, cmap=..., norm=...)` |
| `Map` / `SpectrumMap.integrate()` etc. | `ContourMapPlot` | `toPlot(log=..., zRange=...)` |
| `PLE` | `ContourPlot` | `toPlot(log=True)` |

You can also construct plot classes directly from `fepydas.constructors.Plots`, e.g.
`MultiSpectrumScatterPlot([a, b], ["A", "B"])` to overlay spectra with different axes, or
`SpectrumScatterPlotWithFit(spectrum, fit)` to show data and fit together. Common styling
methods on every plot: `setXRange`, `setYRange`, `setXLog`, `setYLog`, `legend`, `show`, `save`.

---

## Exporting and saving

```python
spectrum.export("spectrum.txt")     # tab-separated text (X + Y columns, with names/units)
series.export("series.txt")         # shared X plus one column per key
map.export("map.txt")               # one column per y position

spectrum.saveBinary("spectrum.bin") # pickle the whole object
# reload with Binary("spectrum.bin")
```

For fit output alongside the data, use the writers in `fepydas.workers.ASCIIWriter`:

```python
from fepydas.workers.ASCIIWriter import SpectrumWithFitWriter
SpectrumWithFitWriter("plot.txt", spectrum, fit, withIRF=True)   # writes data + fit (+ IRF)
```

---

## Batch processing whole directories

`DirectoryLoader` applies one reader to every file in a folder and lets you run the same
operation on all of them:

```python
from fepydas.workers.DirectoryLoader import DirectoryLoader
from fepydas.constructors.datareaders.Custom import ASCII_Gelbes
from fepydas.datatypes.Dataset import JoinSpectrumSeries

loader = DirectoryLoader(ASCII_Gelbes)
loader.loadFromDirectory("data/", lambda name: name.endswith(".dat"))
loader.callFunction("cutAxisByIdx", 50, 3000)      # call .cutAxisByIdx(50, 3000) on each

series = JoinSpectrumSeries(loader.getList(), interpol=True, names=loader.getKeys())
```

### Rebinning a series

```python
from fepydas.constructors.Binner import LinearBinner   # also LogBinner, InverseBinner
from fepydas.datatypes.Dataset import BinnedSpectrumSeries

binned = BinnedSpectrumSeries(series, LinearBinner(), num_bins=200)
```

---

## Worked examples

The [`examples/`](examples/) directory contains complete, runnable scripts (each with its own
data files). Run a single one from its folder, e.g. `cd examples/PLE && python3 PLE.py`, or run
them all with `cd examples && python3 RunExamples.py`.

| Example | Demonstrates |
|---------|--------------|
| [`PLE/`](examples/PLE) | Full PLE pipeline: calibration, vacuum correction, lamp-response correction, contour plots |
| [`Binning/`](examples/Binning) | Temperature series, Arrhenius transform, `BinnedSpectrumSeries`, integration |
| [`EQE/`](examples/EQE) | EQE analysis with custom fit functions and `SpectrumScatterPlotWithFit` |
| [`HighDynamicRange/`](examples/HighDynamicRange) | Joining differently-exposed spectra into one HDR spectrum |
| [`Mapscan/`](examples/Mapscan) & [`NewMapscan/`](examples/NewMapscan) | Spectrum maps: integration, per-pixel fitting, clustering |
| [`Histogram_IRF_Convolution_Fitting/`](examples/Histogram_IRF_Convolution_Fitting) | TCSPC decay fitting with IRF convolution |
| [`PQ_Histograms/`](examples/PQ_Histograms) | Reading PicoHarp/TimeHarp histograms |
| [`SSTRPL/`](examples/SSTRPL) | Component removal/decomposition and exponential fitting |
| [`RamanAngleDecomposition/`](examples/RamanAngleDecomposition) | Raman angle series, LDA decomposition, polar projection plots |

---

## API map

| Import | Contents |
|--------|----------|
| `fepydas.datatypes` | `DataType`, `generateDataType`, predefined types (`WL_nm`, `E_eV`, ...) |
| `fepydas.datatypes.Data` | `Data1D/2D/3D`, `Unit`, `DataType`, `Transformation`, `VacuumCorrection`, `Response` |
| `fepydas.datatypes.Dataset` | `Spectrum`, `SpectrumSeries`, `PLE`, `SpectrumMap`, `SparseSpectrumMap`, `Map`, `SpectrumWithIRF`, ... |
| `fepydas.constructors.datareaders.Generic` | `GenericXY`, `ASCII`, `Histogram`, `HistogramWithIRF`, `Binary`, ... |
| `fepydas.constructors.datareaders.Custom` | `ASCII_Spectrum`, `ASCII_PLE`, `ASCII_EQE`, `Mapscan`, ... |
| `fepydas.constructors.datareaders.Proprietary` | `PicoHarpHistogram`, `TimeHarpHistogram`, `BeckerHicklHistogram`, `PTUHistogram` |
| `fepydas.constructors.datareaders.FromLabview` | `Automatic` |
| `fepydas.constructors.Plots` | `SpectrumPlot`, `SpectrumSeriesPlot`, `ContourPlot`, `ContourMapPlot`, ... |
| `fepydas.constructors.Binner` | `LinearBinner`, `LogBinner`, `InverseBinner` |
| `fepydas.workers.Fit` | `Fit`, `GaussianFit`, `ExponentialFit`, `Calibration`, `FitSimple`, ... |
| `fepydas.workers.ASCIIWriter` | `MultiSpectrumWriter`, `SpectrumWithFitWriter` |
| `fepydas.workers.DirectoryLoader` | `DirectoryLoader` |

Every class and function carries a detailed docstring (visible as a tooltip in most editors)
describing its parameters, options and return value.

---

## Requirements

- Python 3 (3.9+ recommended)
- numpy ≥ 2.0, scipy, matplotlib, lmfit, scikit-learn, pyamg, sympy ≥ 1.13.3, pathos, tqdm, ptufile

These are installed automatically by `pip install fepydas`.

---

*fepydas is developed at TU Berlin. Bug reports and contributions:
<https://git.tu-berlin.de/nippert/fepydas>.*
