Skip to main content

Symmetrisation of EULUMDAT (.ldt) photometric files — extension to eulumdat-py.

Project description

eulumdat-symmetry

An extension to pyldt for symmetrising EULUMDAT (.ldt) photometric files.

Developed in an ISO 17025 accredited photometry laboratory.


Overview

EULUMDAT files store luminous intensity distributions as a [mc × ng] matrix of C-planes and γ-angles. When a luminaire has a symmetric distribution, the file can declare this via the ISYM field (1–4), which allows lighting design software to reconstruct the full distribution from a subset of C-planes.

This library provides two tools:

  • LdtSymmetriser — symmetrise a pyldt.Ldt object for a known ISYM mode. Deterministic, no configuration needed.
  • LdtAutoDetector — automatically detect the most plausible ISYM mode from the shape of the distribution. Optional; use only when the mode is not known in advance.

Installation

pip install eulumdat-symmetry

Requires eulumdat-py >= 0.1.4 (pyldt).


ISYM codes

ISYM Description C-planes symmetrised
0 No symmetry
1 Full rotational symmetry All planes averaged to a single profile
2 Symmetry about C0–C180 C and 360°−C averaged
3 Symmetry about C90–C270 C and 180°−C averaged
4 Quadrant symmetry All 4 quadrant mirrors averaged

Usage

Known symmetry mode

from pyldt import LdtReader, LdtWriter
from ldt_symmetry import LdtSymmetriser

ldt = LdtReader.read("luminaire.ldt")
ldt_sym = LdtSymmetriser.symmetrise(ldt, isym=2)
LdtWriter.write(ldt_sym, "luminaire_SYM.ldt")

LdtSymmetriser.symmetrise() returns a new Ldt object — the original is never modified.

If the file already declares a non-zero ISYM, the function returns an unchanged copy by default (the distribution has already been symmetrised at measurement time). Use force=True to re-symmetrise regardless.

ldt_sym = LdtSymmetriser.symmetrise(ldt, isym=2, force=True)

Automatic detection

from pyldt import LdtReader, LdtWriter
from ldt_symmetry import LdtAutoDetector, LdtSymmetriser

ldt = LdtReader.read("luminaire.ldt")

detector = LdtAutoDetector()
isym = detector.detect(ldt)

if isym != 0:
    ldt_sym = LdtSymmetriser.symmetrise(ldt, isym=isym)
    LdtWriter.write(ldt_sym, "luminaire_SYM.ldt")
else:
    print("No plausible symmetry detected — file left unchanged.")

Automatic detection with diagnostic

When return_diag=True, detect() returns a (isym, diag) tuple. The diag dict contains the shape metrics, symmetry scores, and the veto decision — useful for understanding why a particular mode was accepted or rejected.

isym, diag = detector.detect(ldt, return_diag=True)

print(f"Detected ISYM: {isym}")
print(f"Candidate ISYM (shape stage): {diag['isym_candidate']}")
print(f"Accepted: {diag['accepted']}")
print(f"Scores: {diag['scores']}")
# diag['scores'] = {1: 0.12, 2: 0.08, 3: 0.31, 4: 0.31}
# diag['score_threshold'] = 0.23

Batch processing

from pathlib import Path
from pyldt import LdtReader, LdtWriter
from ldt_symmetry import LdtAutoDetector, LdtSymmetriser

detector = LdtAutoDetector()

for path in Path("ldt_files").glob("*.ldt"):
    ldt = LdtReader.read(path)
    isym = detector.detect(ldt)
    if isym != 0:
        ldt_sym = LdtSymmetriser.symmetrise(ldt, isym=isym)
        out = path.with_stem(path.stem + "_SYM")
        LdtWriter.write(ldt_sym, out)
        print(f"{path.name} → ISYM {isym}{out.name}")
    else:
        print(f"{path.name} → no symmetry detected")

How automatic detection works

Detection runs in two stages:

Stage 1 — Shape analysis

The intensity distribution is projected onto the floor (γ < 90°) and ceiling (γ > 90°) planes using the point-source illuminance formula E = I·cos³(γ)/d². Weighted moments (centroid offset, ellipse anisotropy, principal axis orientation) and angular harmonics (H4 for quadrant detection) are computed for each hemisphere. A candidate mode is chosen by combining the decisions from both hemispheres.

Stage 2 — Score veto

The relative RMS asymmetry is measured for the candidate mode. If the score exceeds the threshold, the candidate is rejected and ISYM 0 is returned. This prevents the shape stage from proposing a symmetric mode when the distribution is actually irregular.

Both stages must agree for a mode to be accepted.

Validation

The detection algorithm was validated on a dataset of 42 real manufacturer LDT files covering all five ISYM modes (0–4), selected for their representativeness from several hundred laboratory measurements. No misclassification was observed (42/42 correct).


Adjusting detection thresholds

The default thresholds work well for typical manufacturer files. If you observe incorrect decisions on your dataset, you can adjust them at instantiation:

detector = LdtAutoDetector(
    score_th=0.20,      # stricter veto for ISYM 2/3/4 (default: 0.23)
    rot_score_th=0.15,  # stricter veto for ISYM 1 (default: 0.20)
)

See the LdtAutoDetector docstring for the full list of parameters.


Project structure

ldt_symmetry/
├── __init__.py          ← Public API (LdtSymmetriser, LdtAutoDetector)
├── symmetriser.py       ← LdtSymmetriser
├── auto_detector.py     ← LdtAutoDetector
└── _geometry.py         ← Internal geometry and scoring functions

Relation to pyldt

eulumdat-symmetry is an extension to pyldt, not a replacement. It operates on pyldt.Ldt objects and delegates all file I/O to pyldt.LdtReader and pyldt.LdtWriter.

Task Tool
Read / write .ldt files pyldt (LdtReader, LdtWriter)
Edit header fields pyldt (LdtHeader)
Symmetrise the intensity distribution eulumdat-symmetry (LdtSymmetriser)
Detect the symmetry mode automatically eulumdat-symmetry (LdtAutoDetector)

Source code

github.com/123VincentB/ldt_symmetry


License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eulumdat_symmetry-1.0.0.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

eulumdat_symmetry-1.0.0-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file eulumdat_symmetry-1.0.0.tar.gz.

File metadata

  • Download URL: eulumdat_symmetry-1.0.0.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.11

File hashes

Hashes for eulumdat_symmetry-1.0.0.tar.gz
Algorithm Hash digest
SHA256 1e81c37dbd32eea4c5df6c642f5461ef51e2bcfa2d2b569aca8c71b85f42e0c2
MD5 91e688e8d37d343aa8a0e7332f389649
BLAKE2b-256 bda69d9fb0efc36459496813e82c32f6568235ad8322782d82936706fc58070a

See more details on using hashes here.

File details

Details for the file eulumdat_symmetry-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for eulumdat_symmetry-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 db6b4e69ccacccbbbdfd546f2b43c7f7b99ba6414d8697dd2b373fe5b8500e1c
MD5 8584037de8a4a12cc0606e3804281792
BLAKE2b-256 f127531d9c59f8a5cca6812a79866728ad21470b4c5a07852c410f71a9e91c29

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page