sda.analysis ============ .. py:module:: sda.analysis .. autoapi-nested-parse:: sda.analysis — shared pipeline utilities for SDA data analysis scripts. Modules ------- columns Column-name registries (ENERGY_COLS, VOLTAGE_COLS, …) and row-value helpers (:func:`~sda.analysis.columns.first_value`, :func:`~sda.analysis.columns.concat_notes`). generators Generator-name resolution (:func:`~sda.analysis.generators.get_row_generator`). calibration E = k·V² calibration and energy inference (:data:`~sda.analysis.calibration.K_DEFAULT`, :func:`~sda.analysis.calibration.calibrate_k`, :func:`~sda.analysis.calibration.infer_energy`). loaders Supplementary xlsx file loader (:func:`~sda.analysis.loaders.load_supplementary_xlsx`). scorers Shared score parsers (:func:`~sda.analysis.scorers.parse_plasma_homogene`). viz Reusable Plotly building-blocks (:func:`~sda.analysis.viz.iso_power_traces`, :func:`~sda.analysis.viz.background_heatmap_trace`). Submodules ---------- .. toctree:: :maxdepth: 1 /_api/sda/analysis/calibration/index /_api/sda/analysis/columns/index /_api/sda/analysis/generators/index /_api/sda/analysis/loaders/index /_api/sda/analysis/scorers/index /_api/sda/analysis/viz/index Attributes ---------- .. autoapisummary:: sda.analysis.K_DEFAULT sda.analysis.ENERGY_COLS sda.analysis.FREQ_COLS sda.analysis.GAP_COLS sda.analysis.GEN_COL_PRIORITY sda.analysis.GEN_GENERIC sda.analysis.GENERATOR_NAMES sda.analysis.NOTE_COLS sda.analysis.VOLTAGE_COLS Functions --------- .. autoapisummary:: sda.analysis.calibrate_k sda.analysis.infer_energy sda.analysis.concat_notes sda.analysis.first_value sda.analysis.get_row_generator sda.analysis.load_supplementary_xlsx sda.analysis.parse_plasma_homogene sda.analysis.background_heatmap_trace sda.analysis.iso_power_traces Package Contents ---------------- .. py:data:: K_DEFAULT :type: float :value: 0.07 .. py:function:: calibrate_k(tests, voltage_cols = None, energy_cols = None, exclude = frozenset()) Fit k = E / V² per generator from tests that have both columns. :param tests: :type tests: :py:class:`List` of :py:class:`SDA test names` to :py:class:`scan.` :param voltage_cols: :data:`~sda.analysis.columns.VOLTAGE_COLS`. :type voltage_cols: :py:class:`Column name candidates for voltage. Defaults to` :param energy_cols: :data:`~sda.analysis.columns.ENERGY_COLS`. :type energy_cols: :py:class:`Column name candidates for energy. Defaults to` :param exclude: not meaningful). :type exclude: :py:class:`Test names` to :py:class:`skip (e.g. mixed NRP+DC tests whose k is` :returns: * :py:class:`dict mapping generator name (and `````"default"``:py:class:```)` to :py:class:`median k (mJ / kV²).` * :py:class:`Falls back` to :py:class:``````{"default": K_DEFAULT}``:py:class:``` when no calibration data is found.` .. py:function:: infer_energy(voltage_kv, k_map = None, generator = 'default') Compute E = k · V² using the best available k for the given generator. :param voltage_kv: :type voltage_kv: :py:class:`Voltage in kV.` :param k_map: :func:`calibrate_k`). When ``None``, uses ``K_DEFAULT``. :type k_map: :py:class:`Dict mapping generator names` to :py:class:`k values (from` :param generator: in *k_map*. :type generator: :py:class:`Generator name. Falls back` to :py:class:``````"default"``:py:class:``` when not found` :rtype: :py:class:`Energy per pulse in mJ`, or ``np.nan`` if *voltage_kv* is not finite. .. py:data:: ENERGY_COLS :type: list[str] :value: ['Energie par pulse (mJ)', 'Energie par pulse mesuré (mJ)', 'Energie déposée par pulse (mJ)',... .. py:data:: FREQ_COLS :type: list[str] :value: ['Fréquence (kHz)', 'Frequency (kHz)', 'Input frequency [kHz]'] .. py:data:: GAP_COLS :type: list[str] :value: ['Gap (mm)'] .. py:data:: GEN_COL_PRIORITY :type: list[str] :value: ['Generator', 'Générateur', 'Generateur'] .. py:data:: GEN_GENERIC :type: frozenset[str] .. py:data:: GENERATOR_NAMES :type: list[str] :value: ['SOLO2', 'SOLO3', 'SOLO4', 'SOLO5', 'LOCA', 'ROCO', 'NRP', 'DC', 'HYBRIDE'] .. py:data:: NOTE_COLS :type: list[str] :value: ['Notes', 'Notes2', 'Remarks', 'Commentaires', 'Remarques', 'Justification_Zone', 'Objectifs'] .. py:data:: VOLTAGE_COLS :type: list[str] :value: ['Voltage input (kV)', 'Voltage (kV)', 'Input voltage [kV]', 'Tension (kV)'] .. py:function:: concat_notes(row, df_cols, note_cols = None, *, keyword_filter = None) Concatenate all non-null note/comment fields for hover tooltips. :param row: :type row: :py:class:`A single DataFrame row.` :param df_cols: :type df_cols: :py:class:`Column names present in the DataFrame.` :param note_cols: :data:`NOTE_COLS`. :type note_cols: :py:class:`Ordered list` of :py:class:`note column names. Defaults to` :param keyword_filter: keyword appears in the text (case-insensitive). Pass ``None`` to include all non-empty fields. :type keyword_filter: :py:class:`If given (e.g. `````"pdc"``:py:class:```)`, :py:class:`only include a field when that` :rtype: :py:class:``````"field1 | field2 | …"``:py:class:```` or ``None`` when nothing was found. .. py:function:: first_value(row, df_cols, candidates) Return the first non-null string value from an ordered list of column candidates. :param row: :type row: :py:class:`A single DataFrame row (pd.Series).` :param df_cols: :type df_cols: :py:class:`List` of :py:class:`column names present in the DataFrame.` :param candidates: :type candidates: :py:class:`Ordered list` of :py:class:`column names` to :py:class:`try.` :rtype: :py:class:`The stripped string value` of :py:class:`the first populated column`, or ``None``. .. py:function:: get_row_generator(row, df_cols, test_name, df) Return the most specific generator name for a single DataFrame row. Resolution priority ------------------- 1. First *specific* (non-generic) value from :data:`~sda.analysis.columns.GEN_COL_PRIORITY`. 2. If only a generic family name (e.g. ``"SOLO"``) was found, refine it using ``"Modèle de générateur"`` which carries per-row detail (e.g. ``"SOLO 5"``). 3. Keyword scan of filenames / test name. 4. ``"unknown"``. .. py:function:: load_supplementary_xlsx(test_name, filename = None, glob_patterns = None, sheet = 0, label = None) Load a supplementary ``.xlsx`` from the folder of an SDA test. Copies the file to a temporary location first so that Windows file locks (e.g. the file is open in Excel) do not prevent reading. :param test_name: the folder via :func:`sda.api.file_discovery.resolve_local_test_path`. :type test_name: :py:class:`SDA test identifier (e.g. `````"T346"``:py:class:```). Used` to :py:class:`resolve` :param filename: When ``None``, the first file matching *glob_patterns* is used. :type filename: :py:class:`Exact filename` to :py:class:`load (e.g. `````"T346_analyse_EP.xlsx"``:py:class:```).` :param glob_patterns: *filename* is not given. Defaults to ``["*analyse*.xlsx", "*EP*.xlsx", "*ep*.xlsx"]``. :type glob_patterns: :py:class:`Glob patterns relative` to :py:class:`the test folder` to :py:class:`search when` :param sheet: :type sheet: :py:class:`Sheet index` or :py:class:`name` to :py:class:`parse. Defaults` to ``0`` (first sheet). :param label: ``"-sup"``). :type label: :py:class:`Short label for log messages (defaults to` :rtype: :class:`pandas.DataFrame` — empty on any failure. .. py:function:: parse_plasma_homogene(value) Convert a ``'Plasma homogène'`` cell to a **homogeneity** score in [0, 1]. Scoring convention (0 = non-homogeneous / lensing, 1 = fully homogeneous): ========= ===== Cell value Score ========= ===== Oui 1.0 Partiel 0.5 Non 0.0 ========= ===== All other values (empty, ``—``, unknown text) → ``np.nan``. .. note:: Analysis scripts that need a *different* mapping (e.g. mapping ``Oui`` to a carbon-bridge time score) should implement their own thin wrapper rather than modifying this function. .. py:function:: background_heatmap_trace(x_pts, y_pts, z_pts, x_range, y_range, colorscale = 'RdYlGn', zmin = None, zmax = None, grid_size = 80, opacity = 0.4) Return a griddata-interpolated ``go.Heatmap`` trace, or ``None``. Requires at least **3 data points** to interpolate; returns ``None`` when that threshold is not met. :param x_pts: :type x_pts: :py:class:`Scatter coordinates` and :py:class:`values.` :param y_pts: :type y_pts: :py:class:`Scatter coordinates` and :py:class:`values.` :param z_pts: :type z_pts: :py:class:`Scatter coordinates` and :py:class:`values.` :param x_range: :type x_range: :py:class:```(min`, :py:class:`max)`` tuples that define the grid extent.` :param y_range: :type y_range: :py:class:```(min`, :py:class:`max)`` tuples that define the grid extent.` :param colorscale: :type colorscale: :py:class:`Plotly colorscale name (default `````"RdYlGn"``:py:class:```).` :param zmin: :type zmin: :py:class:`Color axis limits. Inferred from *z_pts* when ``None``.` :param zmax: :type zmax: :py:class:`Color axis limits. Inferred from *z_pts* when ``None``.` :param grid_size: :type grid_size: :py:class:`Number` of :py:class:`grid points per axis (default 80).` :param opacity: :type opacity: :py:class:`Heatmap opacity (default 0.40).` :rtype: ``go.Heatmap`` or ``None``. .. py:function:: iso_power_traces(x_min, x_max, y_min, y_max, power_candidates = None) Return ``(traces, annotations)`` for iso-power hyperbolas. Each power level P (watts, since kHz·mJ = W) is drawn as a faint dotted line with a semi-transparent annotation label. A wide transparent overlay trace carries a detailed hover tooltip without affecting visuals. :param x_min: :type x_min: :py:class:`Data range for the x-axis (Energy per pulse`, :py:class:`mJ).` :param x_max: :type x_max: :py:class:`Data range for the x-axis (Energy per pulse`, :py:class:`mJ).` :param y_min: :type y_min: :py:class:`Data range for the y-axis (Frequency`, :py:class:`kHz).` :param y_max: :type y_max: :py:class:`Data range for the y-axis (Frequency`, :py:class:`kHz).` :param power_candidates: range are skipped automatically. :type power_candidates: :py:class:`Power levels in W` to :py:class:`consider. Levels outside the data` :returns: * **traces** (:py:class:`List` of ``go.Scatter`` traces to :py:class:`extend into the figure.`) * **annotations** (:py:class:`List` of :py:class:`annotation dicts` to :py:class:`pass` to ``fig.update_layout``.)