optivibe-reference 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. optivibe_reference/__init__.py +14 -0
  2. optivibe_reference/_envelope.py +19 -0
  3. optivibe_reference/_paths.py +66 -0
  4. optivibe_reference/_probe_common.py +142 -0
  5. optivibe_reference/build_data.py +271 -0
  6. optivibe_reference/build_steps.py +182 -0
  7. optivibe_reference/catalog_build.py +1138 -0
  8. optivibe_reference/data/operand_semantics.json +2192 -0
  9. optivibe_reference/data/operand_synonyms.json +1687 -0
  10. optivibe_reference/data/provenance_grants.txt +5 -0
  11. optivibe_reference/data/tolerance_operand_synonyms.json +307 -0
  12. optivibe_reference/errors.py +57 -0
  13. optivibe_reference/glass_build.py +433 -0
  14. optivibe_reference/glass_dispersion.py +207 -0
  15. optivibe_reference/glass_math.py +383 -0
  16. optivibe_reference/glass_parse.py +168 -0
  17. optivibe_reference/manual_build.py +672 -0
  18. optivibe_reference/manual_enrich.py +490 -0
  19. optivibe_reference/probe_operands.py +226 -0
  20. optivibe_reference/probe_tolerances.py +371 -0
  21. optivibe_reference/server.py +547 -0
  22. optivibe_reference/tolerance_build.py +554 -0
  23. optivibe_reference/tools/__init__.py +5 -0
  24. optivibe_reference/tools/_glass_common.py +137 -0
  25. optivibe_reference/tools/_glass_kind.py +133 -0
  26. optivibe_reference/tools/_query_params.py +57 -0
  27. optivibe_reference/tools/_rag_match.py +92 -0
  28. optivibe_reference/tools/find_glass_pair.py +574 -0
  29. optivibe_reference/tools/find_glasses.py +339 -0
  30. optivibe_reference/tools/lookup_glass.py +262 -0
  31. optivibe_reference/tools/lookup_operand.py +463 -0
  32. optivibe_reference/tools/search_reference.py +321 -0
  33. optivibe_reference-0.2.0.dist-info/METADATA +44 -0
  34. optivibe_reference-0.2.0.dist-info/RECORD +37 -0
  35. optivibe_reference-0.2.0.dist-info/WHEEL +5 -0
  36. optivibe_reference-0.2.0.dist-info/entry_points.txt +2 -0
  37. optivibe_reference-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,182 @@
1
+ """build_steps.py — the glass-catalog and manual-corpus build steps, as package code.
2
+
3
+ These were the bodies of ``scripts/build_glass_catalog.py`` and
4
+ ``scripts/build_manual_corpus.py``. They live in the package so an INSTALLED
5
+ optivibe-reference (no ``scripts/`` dir) can run them through ``build_data``
6
+ (``optivibe-build-data``); the two scripts are now thin wrappers with the same CLI.
7
+
8
+ * ``build_glass_catalog()`` — resolve the install Glasscat dir (``ZEMAX_GLASSCAT``
9
+ override, else ``~/Documents/Zemax/Glasscat``), assert the ``.agf``-derivation grant
10
+ (fail-closed), re-parse + recompute every shipped ``.agf`` record, write
11
+ ``data/glass_catalog.json``. No raw ``.agf`` is kept; only OUR computed/parsed numbers
12
+ + factual glass names land in the JSON.
13
+ * ``build_manual_corpus(argv)`` — resolve the licensed install-dir manual PDF
14
+ (``OPTIVIBE_MANUAL_PDF`` else the standard install-dir glob) and build the local FTS5
15
+ ``.db`` + the TEXT-FREE manifest. ``--emit-raw`` ALSO writes the VERBATIM raw
16
+ operand/tolerance extracts (the manual oracle, never committed) into the captures dir.
17
+
18
+ Both print ASCII status ONLY and NEVER relay extracted manual or catalog prose (the
19
+ cp1252 ``conda run`` relay crash, probe finding 3).
20
+ """
21
+ import glob
22
+ import json
23
+ import os
24
+ import sys
25
+
26
+ from . import _paths, glass_build, manual_build
27
+ from .glass_parse import resolve_glasscat
28
+
29
+
30
+ # --- glass catalog ----------------------------------------------------------
31
+ def build_glass_catalog():
32
+ """Build ``data/glass_catalog.json`` from the install Glasscat dir; return 0.
33
+
34
+ Raises ``FileNotFoundError`` when no Glasscat dir resolves (the caller's SKIP) and
35
+ ``ProvenanceGateError`` when the derivation grant is not on file (a FAIL).
36
+ """
37
+ glasscat = resolve_glasscat()
38
+ print("[glass-build] Glasscat dir: %s" % glasscat)
39
+
40
+ catalog = glass_build.build_glass_catalog_json(
41
+ glasscat, glass_build.PROVENANCE_PATH
42
+ )
43
+ glass_build.write_glass_catalog_json(glass_build.GLASS_CATALOG_JSON, catalog)
44
+
45
+ rows = catalog["rows"]
46
+ nd_valid = sum(1 for r in rows if r["nd_valid"])
47
+ pg_f_valid = sum(1 for r in rows if r["pg_f_valid"])
48
+ nd_null = sum(1 for r in rows if r["nd"] is None)
49
+ print("[glass-build] agf_glass_count: %d" % catalog["agf_glass_count"])
50
+ print("[glass-build] rows written: %d" % len(rows))
51
+ print("[glass-build] nd_valid rows: %d" % nd_valid)
52
+ print("[glass-build] pg_f_valid rows: %d" % pg_f_valid)
53
+ print("[glass-build] nd-null (out-of-band) rows: %d" % nd_null)
54
+ print("[glass-build] wrote -> %s" % glass_build.GLASS_CATALOG_JSON)
55
+ return 0
56
+
57
+
58
+ # --- manual corpus ----------------------------------------------------------
59
+ def raw_extract_path():
60
+ """The GITIGNORED verbatim merit-operand raw extract (the oracle)."""
61
+ return os.path.join(_paths.captures_dir(), "operand_raw_descriptions.json")
62
+
63
+
64
+ def tol_raw_extract_path():
65
+ """The GITIGNORED verbatim tolerance-operand raw extract (the oracle)."""
66
+ return os.path.join(_paths.captures_dir(), "tolerance_raw_descriptions.json")
67
+
68
+
69
+ def find_manual_pdf():
70
+ """Resolve the licensed manual PDF, or return None (genuine absence)."""
71
+ override = os.environ.get("OPTIVIBE_MANUAL_PDF")
72
+ if override and os.path.isfile(override):
73
+ return override
74
+ for base in (
75
+ os.environ.get("ProgramFiles", r"C:\Program Files"),
76
+ os.environ.get("ProgramFiles(x86)", r"C:\Program Files (x86)"),
77
+ ):
78
+ hits = glob.glob(
79
+ os.path.join(base, "*Zemax OpticStudio*", "OpticStudio_UserManual_en.pdf")
80
+ )
81
+ if hits:
82
+ return sorted(hits, reverse=True)[0]
83
+ return None
84
+
85
+
86
+ def write_raw(path, entries, order):
87
+ """Write ``{code: {code, page, raw_text}}`` to a GITIGNORED capture (oracle).
88
+
89
+ NEVER prints the extracted prose — the caller prints only the entry count.
90
+
91
+ ATOMIC (a hardening finding): the oracle is written to a same-dir
92
+ ``<path>.tmp`` then ``os.replace``d onto the final path, so a concurrent reader
93
+ never sees a half-written raw extract.
94
+ """
95
+ os.makedirs(os.path.dirname(path), exist_ok=True)
96
+ payload = {code: entries[code] for code in order}
97
+ tmp_path = path + ".tmp"
98
+ try:
99
+ with open(tmp_path, "w", encoding="utf-8", newline="\n") as fh:
100
+ json.dump(payload, fh, indent=2, ensure_ascii=True)
101
+ fh.write("\n")
102
+ os.replace(tmp_path, path) # atomic same-dir rename
103
+ except Exception:
104
+ try:
105
+ os.remove(tmp_path)
106
+ except OSError:
107
+ pass
108
+ raise
109
+ return payload
110
+
111
+
112
+ def emit_raw(pdf_path):
113
+ """Extract BOTH raw operand oracles to the GITIGNORED captures (verbatim source).
114
+
115
+ Merit: the finding-2 bbox two-column pairer over the alphabetical §5.2.1.3
116
+ section -> ``operand_raw_descriptions.json``. Tolerance: the NEW heading-delimited
117
+ §7.2.1.1 pairer -> ``tolerance_raw_descriptions.json`` (a SEPARATE
118
+ strategy). NEVER prints the extracted prose — only entry counts.
119
+ """
120
+ # Lazy: manual_enrich imports manual_build, and only this path needs it.
121
+ from . import manual_enrich
122
+
123
+ # The merit operand INVENTORY gates the glued ``CODE(`` split (an entry
124
+ # opens only when the prefix is a real operand, never on prose like ``RMS(``).
125
+ # ONE shared loader — no second path/list here.
126
+ merit_valid_codes = manual_enrich.load_merit_operand_inventory()
127
+ merit_pages = manual_enrich.iter_operand_pages(pdf_path)
128
+ merit_entries, merit_order = manual_enrich.pair_operands(
129
+ merit_pages, valid_codes=merit_valid_codes
130
+ )
131
+ merit_path = raw_extract_path()
132
+ merit_payload = write_raw(merit_path, merit_entries, merit_order)
133
+ print("raw-extract: wrote {} operand entries -> {}".format(
134
+ len(merit_payload), merit_path))
135
+
136
+ # Pass the tolerance INVENTORY code set so a phantom heading token
137
+ # (``IMPORTANT NOTE``) or a swallowed title word is dropped at source, never
138
+ # admitted as a bogus code into the raw oracle.
139
+ tol_valid_codes = set(manual_enrich.load_tolerance_operand_inventory())
140
+ tol_pages = manual_enrich.iter_tolerance_operand_pages(pdf_path)
141
+ tol_entries, tol_order = manual_enrich.pair_tolerance_operands(
142
+ tol_pages, valid_codes=tol_valid_codes
143
+ )
144
+ tol_path = tol_raw_extract_path()
145
+ tol_payload = write_raw(tol_path, tol_entries, tol_order)
146
+ print("raw-extract: wrote {} tolerance entries -> {}".format(
147
+ len(tol_payload), tol_path))
148
+
149
+
150
+ def build_manual_corpus(argv=None, find_pdf=None):
151
+ """Build the manual corpus (and, with ``--emit-raw``, the raw oracles); return 0.
152
+
153
+ ``find_pdf`` defaults to ``find_manual_pdf``; the script wrapper passes its own
154
+ module-level name so a caller patching the wrapper still steers this function. On
155
+ genuine PDF absence (license-free CI) it prints an ASCII status and returns 0 — the
156
+ only allowed skip of the live build.
157
+ """
158
+ argv = list(sys.argv[1:] if argv is None else argv)
159
+ emit = "--emit-raw" in argv
160
+
161
+ pdf = (find_pdf or find_manual_pdf)()
162
+ if pdf is None:
163
+ print(
164
+ "manual PDF not present (set OPTIVIBE_MANUAL_PDF or install OpticStudio); "
165
+ "skipping corpus build (license-free CI has no PDF)."
166
+ )
167
+ return 0
168
+
169
+ print("building manual corpus from the install-dir PDF ...")
170
+ result = manual_build.build_manual_db(pdf)
171
+ if result.get("skipped"):
172
+ print("corpus: up-to-date (content-addressed skip); nothing to rebuild.")
173
+ else:
174
+ print("corpus: pages={} chunks={}".format(
175
+ result["pdf_page_count"], result["chunk_count"]))
176
+ print("corpus db -> {}".format(result["db_path"]))
177
+ print("manifest -> {}".format(result["manifest_path"]))
178
+
179
+ if emit:
180
+ emit_raw(pdf)
181
+
182
+ return 0