Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e374939
fix: modernize isinstance type checks
kegouro Jul 13, 2026
d665e52
Corrige narrowing de páginas JPK TIFF
kegouro Jul 13, 2026
f7deb44
fix: unificar versión del paquete
kegouro Jul 13, 2026
8c3d58f
test: verificar valores exactos de Sa y Sq
kegouro Jul 13, 2026
bc01805
test: verifica plane_fit no destructivo
kegouro Jul 13, 2026
6b7e431
feat: añade SHA-256 a proyectos spmproj
kegouro Jul 13, 2026
52299f7
test: verify roughness CSV and JSON round trip
kegouro Jul 13, 2026
df91a70
build: excluye GUI legacy de artefactos
kegouro Jul 13, 2026
43c3e28
chore: unblock full mypy check
kegouro Jul 13, 2026
29afdff
test(gui): aislar recursos Qt entre flujos E2E
kegouro Jul 13, 2026
d4e04da
fix: preserve JPK frames and unhashed projects
kegouro Jul 13, 2026
820df23
Fortalece fronteras cientificas de imagen
kegouro Jul 13, 2026
624044a
Normaliza base polinomial de nivelado
kegouro Jul 13, 2026
149c675
Añade selección inequívoca al CLI de imagen
kegouro Jul 13, 2026
c76bb8a
Valida contrato sintético NHF
kegouro Jul 13, 2026
31e70a2
feat(cli): añadir exportación de perfiles
kegouro Jul 13, 2026
34e24e6
test(e2e): aislar identidades de journeys
kegouro Jul 13, 2026
adcd5e5
test(e2e): caracteriza journey GUI de imagen
kegouro Jul 13, 2026
1973d8c
docs: actualiza journey de imagen
kegouro Jul 13, 2026
1fff984
docs: corrige extras y ejemplo de exportación
kegouro Jul 13, 2026
6266d50
chore: sanear colección opcional de GUI
kegouro Jul 13, 2026
175f62c
chore: cierra gates de Slice B
kegouro Jul 13, 2026
43456ae
test: estabiliza salida Rich en CI
kegouro Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
(cono) → módulo de Young, adhesión.

### Formatos e interop
- Lectura `.nid` (validada con archivos del lab), `.nhf` (HDF5), `.gwy`.
- Lectura `.nid` (validada externamente), `.nhf` experimental (contrato HDF5
sintético) y `.gwy` (round-trip).
- Escritura `.gwy` (round-trip con Gwyddion) + "Abrir en Gwyddion".
- Exportación CSV / JSON / HDF5 / PNG / SVG / PDF.

Expand Down Expand Up @@ -45,7 +46,7 @@
- [ ] FFT / análisis de PSD de rugosidad.

### Formatos
- [ ] Validar `.nhf` con archivos reales (o delegar en NSFopen).
- [ ] Validar `.nhf` con un oráculo externo y evaluar una integración futura con NSFopen.
- [ ] Soporte de más formatos (Bruker, Asylum) vía AFMReader.

### Infra
Expand All @@ -56,7 +57,7 @@

| Proyecto | Uso potencial |
|----------|---------------|
| [NSFopen](https://pypi.org/project/NSFopen/) | Lector `.nid`/`.nhf` validado (fallback) |
| [NSFopen](https://pypi.org/project/NSFopen/) | Integración futura evaluable para `.nid`/`.nhf` |
| [gwyfile](https://pypi.org/project/gwyfile/) | Interop `.gwy` (adoptado) |
| [cmcrameri](https://github.com/callumrollo/cmcrameri) | Colormaps perceptuales (adoptado) |
| [matplotlib-scalebar](https://pypi.org/project/matplotlib-scalebar/) | Barra de escala (adoptado) |
Expand Down
12 changes: 12 additions & 0 deletions docs/VALIDATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ del instrumento de forma correcta, para dar confianza científica a los
resultados. Las pruebas viven en `tests/validation/` y se ejecutan con
`pytest tests/validation` (se omiten si no están los archivos del lab).

## Alcance por formato

| Formato | Evidencia disponible | Estado |
|---------|----------------------|--------|
| `.nid` | Comparación externa contra exportaciones de Gwyddion | Validado externamente |
| `.gwy` | Escritura y relectura con igualdad de datos | Round-trip reproducible |
| `.nhf` | Archivos HDF5 sintéticos que ejercitan el contrato público | Experimental |

El lector `.nhf` conserva datos y atributos del esquema genérico que recibe,
ignora datasets que no son 2D y reporta archivos ilegibles. Estas pruebas no
constituyen validación contra un instrumento ni contra un oráculo externo.

## 1. Validación contra ground truth (Gwyddion)

Se comparó la lectura del `.nid` (crudo del instrumento) contra el `.gwy`
Expand Down
96 changes: 74 additions & 22 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ spmkit expone una API pública limpia en `spmkit.core`. La CLI y la GUI solo orq

```bash
pip install spmkit
pip install "spmkit[gwy,hdf5]"
```

La instalación base cubre `.nid`. Los ejemplos `.gwy` requieren el extra `gwy`; el extra
`hdf5` habilita el lector experimental `.nhf` y la exportación HDF5.

---

## Carga de datos — `spmkit.load`
Expand All @@ -18,7 +22,7 @@ Punto de entrada principal. Detecta el formato automáticamente.
from spmkit import load

data = load("scan.nid") # NanoSurf clásico
data = load("scan.nhf") # NanoSurf HDF5
data = load("scan.nhf") # NanoSurf HDF5 (lector experimental)
data = load("scan.gwy") # Gwyddion
```

Expand All @@ -30,17 +34,25 @@ El objeto devuelto por `load()`.
# Ver canales disponibles
print(data.names) # ['Z-Axis', 'CPD', 'Phase', ...]

# Acceder a un canal (devuelve SPMChannel)
ch = data["Z-Axis"]
# Selección estricta: debe quedar exactamente una coincidencia
ch = data.select("Z-Axis", direction="forward")
ch = data.select("Z-Axis", direction="forward", group="Topography forward")

# Acceder con dirección explícita
ch = data["Z-Axis", "forward"]
ch = data["Z-Axis", "backward"]
# Compatibilidad: acceso no estricto por nombre
ch = data.get("Z-Axis")
ch = data["Z-Axis"]

# Metadatos del barrido
print(data.metadata) # dict con parámetros del instrumento
```

`select(name, direction=..., group=...)` lanza `KeyError` si no encuentra coincidencias y
`ValueError` si encuentra más de una. Es la opción recomendada cuando hay nombres duplicados.

`get(name, direction="forward")` conserva el acceso histórico: busca esa dirección y, si no
existe, devuelve la primera coincidencia por nombre. `data[name]` equivale a `get(name)`;
ninguna de estas dos formas detecta ambigüedad.

### `SPMChannel`

Representa un canal 2D en unidades físicas.
Expand All @@ -55,6 +67,8 @@ ch.y_range # float, rango vertical en metros
ch.shape # tuple (rows, cols)
ch.name # str, nombre del canal
ch.direction # "forward" | "backward"
ch.group # str, grupo de origen
ch.metadata # dict, metadatos crudos del canal
```

---
Expand All @@ -80,21 +94,22 @@ Todas las funciones devuelven un nuevo `SPMChannel` (inmutable).

## Rugosidad — `spmkit.core.analysis.roughness`

Parámetros ISO 25178.
Estadísticas de rugosidad areal.

```python
from spmkit.core.analysis import roughness

result = roughness.statistics(flat)

result.sa # rugosidad media aritmética
result.sq # rugosidad RMS
result.sz # altura máxima (Sp + Sv)
result.sp # altura máxima de picos
result.sv # profundidad máxima de valles
result.ssk # asimetría (skewness)
result.sku # curtosis (kurtosis)
result.Sa # rugosidad media aritmética
result.Sq # rugosidad RMS
result.Sz # altura máxima (Sp + |Sv|)
result.Sp # altura máxima de picos
result.Sv # profundidad máxima de valles (valor negativo)
result.Ssk # asimetría (skewness)
result.Sku # curtosis (kurtosis)
result.unit # unidad del canal ("m", "nm", …)
result.n_points # puntos finitos usados

# Convertir a dict
d = result.to_dict()
Expand All @@ -110,18 +125,50 @@ Potencial de contacto y función de trabajo.
from spmkit.core.analysis import kpfm

# Estadísticas básicas del canal CPD
result = kpfm.statistics(data["CPD"])
cpd_channel = data.select("CPD", direction="forward")
result = kpfm.statistics(cpd_channel)

result.mean_cpd # CPD medio (V)
result.std_cpd # desviación estándar (V)
result.min_cpd # mínimo (V)
result.max_cpd # máximo (V)
result.mean # CPD medio
result.std # desviación estándar
result.minimum # mínimo
result.maximum # máximo
result.contrast # máximo - mínimo
result.unit # unidad del canal (debe ser V)
result.work_function # None: no se proporcionó la función de trabajo de la punta

# Con función de trabajo de la punta (eV)
result = kpfm.statistics(data["CPD"], tip_work_function=4.8)
result = kpfm.statistics(cpd_channel, tip_work_function=4.7)
result.work_function # función de trabajo de la muestra (eV)
result.work_function_unit # "eV"
```

La relación implementada es `phi_sample = phi_tip - mean(CPD)`. Sin
`tip_work_function`, el resultado conserva `work_function=None`.

---

## Perfiles — `spmkit.core.analysis.profiles`

```python
from spmkit.core.analysis import profiles

profile = profiles.line(
ch,
(0.5, 0.5), # (columna, fila) inicial en píxeles
(5.5, 3.5), # (columna, fila) final en píxeles
n=3,
)

profile.distance # ndarray, distancia física
profile.height # ndarray, valores interpolados del canal
profile.distance_unit # "m"
profile.unit # unidad de altura del canal
len(profile) # número de muestras
```

Ambos extremos deben estar dentro de la imagen. `n=None` elige el número de muestras a
partir de la longitud del segmento en píxeles.

---

## Nanomecánica — `spmkit.core.analysis.mechanics`
Expand Down Expand Up @@ -239,7 +286,7 @@ from spmkit.core.viz import FigureSpec, save_figure

spec = FigureSpec(
title="Topografía AFM",
colormap="batlow", # colormaps Crameri
colormap="gold", # valor por defecto
colorbar_label="Z-Axis (nm)",
)

Expand All @@ -249,7 +296,7 @@ save_figure(flat, spec, "topografia.svg")
save_figure(flat, spec, "topografia.pdf")
```

**Colormaps disponibles** (Crameri perceptualmente uniformes):
Además de `gold`, hay colormaps de matplotlib y Crameri cuando están instalados, por ejemplo:

`batlow`, `tokyo`, `oslo`, `vik`, `davos`, `hawaii`, `lapaz`, `roma`, `turku`, `acton`

Expand All @@ -258,12 +305,17 @@ save_figure(flat, spec, "topografia.pdf")
## Exportación — `spmkit.core.export`

```python
from spmkit.core.analysis import roughness
from spmkit.core.export import to_csv, to_json, to_hdf5

# Exportar resultados de rugosidad
roughness_result = roughness.statistics(flat)
to_csv(roughness_result, "roughness.csv")
to_json(roughness_result, "roughness.json")

# Exportar un perfil: distance[m],height[unidad]
to_csv(profile, "profile.csv")

# Exportar datos completos a HDF5
to_hdf5(data, "scan.h5")
```
Expand Down
Loading
Loading