Skip to main content

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

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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

Supported by

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