diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 07e5bedb..e6f848f9 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -474,20 +474,58 @@ jobs: run: | make -C tot libtotapi_mono.so - - name: Inspect libtotapi_mono.so (8-bpsd invariant hard-gated) - # libtotapi_mono_inspect runs one hard-gate plus several + - name: Inspect libtotapi_mono.so (8-bpsd + no-lib*api.so-deps hard-gated) + # libtotapi_mono_inspect runs TWO hard-gates plus several # observational checks: # - # HARD GATE: BPSD storage symbol count == 8 (one per bpsd module + # HARD GATE 1: BPSD storage symbol count == 8 (one per bpsd module # type: device + equ1d + metric1d + plasmaf + shot + species + # trmatrix + trsource). If the per-module dedup ever fails # and we get 5×8=40 (or any other count), the build fails. # + # HARD GATE 2: No `lib*api.so` runtime dep in the .so's NEEDED + # list (Linux `ldd`) / load commands (macOS `otool -L`). The + # whole point of the mono build is that it has no per-module + # .so deps; if any leak in, fail loudly. + # # Observational (printed, NOT enforced as exit code): - # - ldd / otool -L output (should have no lib*api.so deps; - # visual inspection only until we standardize the regex - # across Linux/macOS). # - exported tot_* symbol list. # - file size. run: | make -C tot libtotapi_mono_inspect + + - name: Set up Python (for the mono equivalence step below) + # The mono-build job is otherwise Python-free; pytest is only + # needed for the equivalence step. Pinning to 3.11 (one of the + # versions the pytest job's matrix exercises) keeps the + # behavior aligned without doubling on both 3.11 + 3.13. + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install pytest deps for the mono equivalence step + run: | + python -m pip install --upgrade pip + pip install pytest pytest-forked pytest-timeout pytest-mock + + - name: 1e-10 equivalence test against libtotapi_mono.so + # Phase 2a deliverable (#201): with the unified + # tot_graphics_stubs_mono.f90 in place, libtotapi_mono.so is + # now dlopen-loadable via Python ctypes. Running the existing + # totlib equivalence fixtures against it verifies the mono + # image is a faithful drop-in replacement for the default + # libtotapi.so on the existing test surface — required for + # CLAUDE.md "equivalence tests at 1e-10 MUST pass." + # + # These fixtures don't exercise BPSD-mediated coupling, so + # this is a regression check that the mono build hasn't + # broken the existing path. The Layer-C BPSD smoke test that + # actually validates monolithic broker sharing is deferred + # to #201 Phase 2b. + env: + PYTHONDONTWRITEBYTECODE: "1" + PYTHONPATH: python + TOTLIB_PATH: ${{ github.workspace }}/tot/libtotapi_mono.so + run: | + python -m pytest python/totlib/tests/test_equivalence.py -v \ + --forked --timeout=120 --timeout-method=signal diff --git a/docs/superpowers/specs/2026-05-03-l7b-ii-bpsd-broker-coupling-design.md b/docs/superpowers/specs/2026-05-03-l7b-ii-bpsd-broker-coupling-design.md index 9e70b21b..491e031a 100644 --- a/docs/superpowers/specs/2026-05-03-l7b-ii-bpsd-broker-coupling-design.md +++ b/docs/superpowers/specs/2026-05-03-l7b-ii-bpsd-broker-coupling-design.md @@ -51,6 +51,68 @@ libeqapi.so 側の `bpsd_put_equ1D` は libtrapi.so 側の `bpsd_get_equ1D` `python/trlib/README.md` の "BPSD ブローカー pull 検証" 節 (両方とも 日本語) を参照。 +### Correction note (2026-05-14) + +`nm` / `otool` による実シップ artifact の検証で、上記 +2026-05-04 のメモが置いた前提「レガシー `Tot` / `libtotapi.so` +は eq + tr + bpsd を同一 .so に co-link している」は、現行ビルドに +ついては **誤り** であることが判明した。実際の `libtotapi.so` は +per-module の `libeqapi.so` / `libtrapi.so` / `libfpapi.so` / +`libtiapi.so` / `libwrxapi.so` を別個の runtime shared library として +依存しており (`otool -L tot/libtotapi.so` で確認)、bpsd storage +シンボルを自身では一切持たない (`nm tot/libtotapi.so | grep +'___bpsd_.*_MOD_.*x_init_flag'` は 0 行)。 + +既存の tot 等価性テスト (tot_demo2014_short / tot_ht6m_short) が +1e-10 で通る理由は、fixture が **BPSD 仲介の profile coupling +パスを exercise していない** からである。等価性テストは +orchestrator fan-out (init/run/get_state/finalize) は検証するが、 +cross-module broker round-trip は通っていない。つまり旧 §0 +の「co-link しているため影響を受けない」という記述は、現状では +「coupling path が test されていないだけで、構造的には同じ +isolation 問題を抱えている」が正しい。 + +採用方針: 上記候補 (a)〜(d) のうち **(a) 共有 .so への再構成** を +採用する。ただし元の表現「共有 .so」は曖昧で、実装は厳密には +「**monolithic libtotapi_mono.so** — per-module PIC オブジェクト +全部 + 単一の bpsd PIC セットを 1 つの image に co-link する +ビルドターゲット」を指す。Codex 独立 review (2026-05-15) で +4 候補は「正しい問い設定ではない — broker semantics は 1 つの +broker-owning runtime image を要求する」と指摘され、5 番目の +オプション (true monolithic image) が技術的にも保守性的にも +最良と判定された。`TotPipeline` は scalar coupling 専用に scope +を狭め、broker-mediated coupling は monolithic image の下でのみ +動作させる。 + +PoC ブランチ `chore/l7b-ii-monolithic-poc` (PR #202、merge +`7d18264e`) が `tot/Makefile` 変更のみで `libtotapi_mono.so` +が **link** することを実証した。BPSD storage シンボルは 1 セット +にデデュープ済 (`nm` で 8 unique シンボル、5×8=40 ではない)。 +ただしこの artifact は **まだ dlopen-loadable ではない**: +~30 個のグラフィクス stub シンボル (`_getkgt_`、`_getkrt_`、 +`_contX_`、`_r2w2b_`、`_text_` 等) が個別 `_graphics_stubs.o` +ファイル固有で、mono ビルドが 1 つの stub ファイルだけ保持する +構成 (最広域の fp の stubs) では未定義のまま残る。Phase 2a で +これら全てを 1 つの新規 `tot/tot_graphics_stubs_mono.f90` に統合 +することで dlopen-loadable にする (= 本 issue (#201) Phase 2a の +core deliverable)。 + +Phase 2a の Linux 互換性は PR #205 (`mono-build` CI job) で既に +カバー済み — Linux ld ブランチ (`--start-group/--end-group` + +`--unresolved-symbols=ignore-all` + `-soname`) も macOS と同等に +動作する。 + +詳細な調査ログ: +`docs/superpowers/specs/2026-05-14-l7b-ii-monolithic-poc-findings.md` + +`("eq","tr")` ルールの実 register と Layer C 統合テストは Phase 2b +(別 PR、#201 内) で行う。Phase 2b では、stale BPSD state 問題 +(本節前段で言及) に対応するため process isolation (`--forked`) +または明示的な broker reset を Layer C テストの前提条件とする。 +また、ルールの register は **mono runtime 選択時の条件付き**にし、 +default `libtotapi.so` path で誤って activate されないようにする +(Codex retrospective 2026-05-15 item 5)。 + --- ## §1. Overview and data flow diff --git a/tot/Makefile b/tot/Makefile index b20be32c..c34525e0 100644 --- a/tot/Makefile +++ b/tot/Makefile @@ -587,9 +587,14 @@ FP_PIC_OBJS_MONO_RAW = $(wildcard ../fp/obj/pic/*.o ../fp/obj/pic/*/*.o) TI_PIC_OBJS_MONO_RAW = $(wildcard ../ti/obj/pic/*.o ../ti/obj/pic/*/*.o) WRX_PIC_OBJS_MONO_RAW = $(wildcard ../wrx/obj/pic/*.o ../wrx/obj/pic/*/*.o) +# Phase 2a (2026-05-18): all 5 per-module _graphics_stubs.o files +# are now EXCLUDED. The unified tot_graphics_stubs_mono.f90 source +# (built into tot/obj/pic/tot_graphics_stubs_mono.o) supplies the +# full union of graphics-stub symbols across eq/tr/fp/ti/wrx, so +# none of the per-module stubs participate in the mono link. EQ_PIC_OBJS_MONO = $(filter-out ../eq/obj/pic/bpsd/% ../eq/obj/pic/eq_graphics_stubs.o,$(EQ_PIC_OBJS_MONO_RAW)) TR_PIC_OBJS_MONO = $(filter-out ../tr/obj/pic/bpsd/% ../tr/obj/pic/tr_graphics_stubs.o,$(TR_PIC_OBJS_MONO_RAW)) -FP_PIC_OBJS_MONO = $(filter-out ../fp/obj/pic/bpsd/%,$(FP_PIC_OBJS_MONO_RAW)) +FP_PIC_OBJS_MONO = $(filter-out ../fp/obj/pic/bpsd/% ../fp/obj/pic/fp_graphics_stubs.o,$(FP_PIC_OBJS_MONO_RAW)) TI_PIC_OBJS_MONO = $(filter-out ../ti/obj/pic/bpsd/% ../ti/obj/pic/ti_graphics_stubs.o,$(TI_PIC_OBJS_MONO_RAW)) WRX_PIC_OBJS_MONO = $(filter-out ../wrx/obj/pic/bpsd/% ../wrx/obj/pic/wrx_graphics_stubs.o,$(WRX_PIC_OBJS_MONO_RAW)) @@ -619,12 +624,18 @@ else SO_SONAME_MONO = -Wl,-soname,libtotapi_mono.so endif -# tot's own PIC objects MINUS tot_graphics_stubs.o. The mono build -# uses fp_graphics_stubs.o instead, which is a strict superset of -# tot's stub set for the symbols both provide (guclip_, gudate_, -# guflsh_, gutime_, pagee_, pages_) plus grd1d_mod/grd2d_mod/draw2d_/ -# eqgout_/move2d_ that some module code paths reference. -OBJS_PIC_MONO := $(filter-out $(OBJDIR_PIC)/tot_graphics_stubs.o,$(OBJS_PIC)) +# tot's own PIC objects MINUS tot_graphics_stubs.o, PLUS the new +# unified mono stubs object. tot_graphics_stubs_mono.f90 is a hand- +# authored union of all 5 per-module _graphics_stubs.f90 files +# (eq + tr + fp + ti + wrx), with each symbol defined exactly once +# and ABI choices documented in the source header (notably GUTIME +# picks the REAL signature; EQGOUT picks the 1-arg signature). It +# is used ONLY by the mono build path; the default libtotapi.so +# continues to use tot_graphics_stubs.o through OBJS_PIC. +OBJS_PIC_MONO := $(filter-out $(OBJDIR_PIC)/tot_graphics_stubs.o,$(OBJS_PIC)) \ + $(OBJDIR_PIC)/tot_graphics_stubs_mono.o + +$(OBJDIR_PIC)/tot_graphics_stubs_mono.o : tot_graphics_stubs_mono.f90 # libtotapi_mono.so depends on the per-module .so files (LIBS_PIC_SO) # as real-file prerequisites — the same prereq pattern as @@ -637,7 +648,7 @@ OBJS_PIC_MONO := $(filter-out $(OBJDIR_PIC)/tot_graphics_stubs.o,$(OBJS_PIC)) # libtotapi_mono.so only re-links if a per-module .so is newer. # Using a .PHONY prereq target here would force unconditional re-link # on every invocation (Bugbot LOW finding on PR #202, 2026-05-14). -libtotapi_mono.so: $(OBJS_PIC) $(LIBS_PIC_SO) +libtotapi_mono.so: $(OBJS_PIC_MONO) $(LIBS_PIC_SO) @if [ -z "$(strip $(EQ_PIC_OBJS_MONO))" ] || [ -z "$(strip $(TR_PIC_OBJS_MONO))" ] || \ [ -z "$(strip $(FP_PIC_OBJS_MONO))" ] || [ -z "$(strip $(TI_PIC_OBJS_MONO))" ] || \ [ -z "$(strip $(WRX_PIC_OBJS_MONO))" ] || [ -z "$(strip $(OBJBPSD_PIC_MONO))" ]; then \ @@ -657,20 +668,31 @@ libtotapi_mono.so: $(OBJS_PIC) $(LIBS_PIC_SO) libtotapi_mono_inspect: libtotapi_mono.so @echo "=== file ===" @file libtotapi_mono.so - @echo "=== link deps (should NOT contain any lib*api.so dependency) ===" - @( ldd libtotapi_mono.so 2>/dev/null || otool -L libtotapi_mono.so ) | grep -vE '^libtotapi_mono\.so:' | grep -iE 'libtotapi_mono|api\.so|bpsd|lib' | head -20 - @echo "=== bpsd MOD-private storage symbols (must be 1 set, not 5) ===" - @nm libtotapi_mono.so | grep -E '__bpsd_.*_MOD_.*x_init_flag' | sort -u - @echo "=== bpsd storage symbol count check (must be 8: device + equ1d + metric1d + plasmaf + shot + species + trmatrix + trsource) ===" + @echo "=== link deps ===" + @( ldd libtotapi_mono.so 2>/dev/null || otool -L libtotapi_mono.so ) + @echo "=== HARD GATE 1: bpsd storage symbol count must be exactly 8 (device + equ1d + metric1d + plasmaf + shot + species + trmatrix + trsource) ===" @count=$$(nm libtotapi_mono.so | grep -E '__bpsd_.*_MOD_.*x_init_flag' | sort -u | wc -l | tr -d ' '); \ if [ "$$count" != "8" ]; then \ - echo " FAIL: expected 8 unique bpsd storage symbols, found $$count"; exit 1; \ + echo " FAIL: expected 8 unique bpsd storage symbols, found $$count"; \ + nm libtotapi_mono.so | grep -E '__bpsd_.*_MOD_.*x_init_flag' | sort -u; \ + exit 1; \ else \ echo " OK: 8 unique bpsd storage symbols (one per bpsd module type)"; \ fi - @echo "=== exported tot_* symbols ===" + @echo "=== HARD GATE 2: no per-module lib*api.so runtime deps (the mono build must be self-contained) ===" + @deps=$$( ( ldd libtotapi_mono.so 2>/dev/null || otool -L libtotapi_mono.so ) \ + | grep -E 'lib(eq|tr|fp|ti|wr|wrx|tot)api\.so' \ + | grep -v 'libtotapi_mono\.so' || true ); \ + if [ -n "$$deps" ]; then \ + echo " FAIL: unexpected per-module lib*api.so deps:"; \ + echo "$$deps"; \ + exit 1; \ + else \ + echo " OK: no per-module lib*api.so runtime deps"; \ + fi + @echo "=== exported tot_* symbols (informational) ===" @nm libtotapi_mono.so | grep -E ' T _?tot_' | head -10 - @echo "=== size ===" + @echo "=== size (informational) ===" @ls -la libtotapi_mono.so # --------------------------------------------------------------------- diff --git a/tot/tot_graphics_stubs_mono.f90 b/tot/tot_graphics_stubs_mono.f90 new file mode 100644 index 00000000..db0bb42c --- /dev/null +++ b/tot/tot_graphics_stubs_mono.f90 @@ -0,0 +1,487 @@ +! tot_graphics_stubs_mono.f90 +! +! Unified graphics stubs for the L-7b-ii monolithic libtotapi_mono.so +! build. Consolidates the symbol union across the five per-module +! _graphics_stubs.f90 files (eq + tr + fp + ti + wrx) so the +! mono build can include exactly ONE stubs object, avoiding the +! duplicate-symbol link errors that arise when more than one +! per-module stubs .o is in the same .so. +! +! NOT used by the default per-module libtotapi.so build path — that +! path links each module's own stubs through that module's +! libXapi.so. This file is referenced only by tot/Makefile's +! libtotapi_mono.so target. +! +! Pattern: hand-authored union, each symbol defined ONCE. Grouped +! below by *primary* provenance so future readers can trace back to +! the originating per-module stubs file. +! +! ABI choices for symbols where the per-module stubs diverge: +! +! * GUTIME — eq/tr/ti/tot declare `SUBROUTINE GUTIME(KK)` with +! `CHARACTER(LEN=*), INTENT(OUT) :: KK`; fp/wrx declare +! `SUBROUTINE GUTIME(T)` with `REAL, INTENT(OUT) :: T` (CPU_TIME +! wrapper). Same ELF symbol `_gutime_`, different caller-side +! type — at first glance an ABI mismatch. +! +! In-tree audit (in-house code review 2026-05-18) of all live +! `CALL GUTIME` sites resolved the apparent conflict: +! - **fp** (e.g. fploop.f90): passes a REAL local — matches our +! stub. +! - **tr** (trmain.f90, trrslt_print.f90): passes `GTCPU1` / +! `GTCPU2`, both declared `REAL` — matches our stub. +! - **eq / ti / tot**: zero live `CALL GUTIME` sites. The +! CHARACTER declarations in their stubs are dead code carried +! for the per-module .so's "every stub exported" hygiene; no +! mono-build caller reaches them. +! +! The REAL ABI is therefore unconditionally correct on the +! reachable surface — no "bounded UB" actually occurs at runtime. +! +! * EQGOUT — eq/tr/fp/ti declare `SUBROUTINE EQGOUT(MODE)` with +! `INTEGER, INTENT(IN) :: MODE`; wrx declares `SUBROUTINE EQGOUT` +! (no args). In-tree audit confirms the 1-arg form is correct: +! - tr passes `0`, eq passes `MSTAT` / `1`, fp/ti pass an integer. +! - **wrx has zero live `CALL EQGOUT` sites** — its no-arg stub +! is dead code, same hygiene rationale as the eq/ti/tot GUTIME +! case above. +! +! * GRD1D — fp/ti expose it as `MODULE grd1d_mod` (symbol +! `___grd1d_mod_MOD_grd1d`); wrx exposes it as a standalone +! `SUBROUTINE GRD1D` (symbol `_grd1d_`). These are TWO DIFFERENT +! ELF symbols (module-qualified vs flat), so no ABI conflict. +! Both are provided below. + +! ====================================================================== +! Section 1: symbols common across most/all modules +! ====================================================================== +! (eq + tr + fp + ti + tot + wrx all declare these — first 7 +! matchable across all 6 stub files) + +REAL FUNCTION GUCLIP(X) + IMPLICIT NONE + DOUBLE PRECISION, INTENT(IN) :: X + GUCLIP = REAL(X) +END FUNCTION GUCLIP + +SUBROUTINE PAGES + IMPLICIT NONE +END SUBROUTINE PAGES + +SUBROUTINE PAGEE + IMPLICIT NONE +END SUBROUTINE PAGEE + +SUBROUTINE GUDATE(KK) + IMPLICIT NONE + CHARACTER(LEN=*), INTENT(OUT) :: KK + KK = ' ' +END SUBROUTINE GUDATE + +SUBROUTINE GUFLSH + IMPLICIT NONE +END SUBROUTINE GUFLSH + +! GUTIME — REAL ABI. All live CALL GUTIME sites (fp + tr) pass a +! REAL local; eq/ti/tot have no live callers. See header note. +SUBROUTINE GUTIME(T) + IMPLICIT NONE + REAL, INTENT(OUT) :: T + CALL CPU_TIME(T) +END SUBROUTINE GUTIME + +! ====================================================================== +! Section 2: 2D plotting stubs (common to eq + tr + fp + ti + wrx) +! ====================================================================== + +SUBROUTINE MOVE2D(X, Y) + IMPLICIT NONE + REAL, INTENT(IN) :: X, Y +END SUBROUTINE MOVE2D + +SUBROUTINE DRAW2D(X, Y) + IMPLICIT NONE + REAL, INTENT(IN) :: X, Y +END SUBROUTINE DRAW2D + +! EQGOUT — 1-arg form. eq/tr/fp/ti pass an integer; wrx has no +! live CALL EQGOUT sites. See header note. +SUBROUTINE EQGOUT(MODE) + IMPLICIT NONE + INTEGER, INTENT(IN) :: MODE +END SUBROUTINE EQGOUT + +! ====================================================================== +! Section 3: tr-unique stubs +! ====================================================================== +! tr/trview.f90 + trrslt_*.f90 reference these on the view-menu / +! result-list paths. The C ABI happy path does not reach them. + +SUBROUTINE VIEWRTLIST(NMAX) + IMPLICIT NONE + INTEGER, INTENT(IN) :: NMAX +END SUBROUTINE VIEWRTLIST + +SUBROUTINE VIEWGTLIST(NMAX) + IMPLICIT NONE + INTEGER, INTENT(IN) :: NMAX +END SUBROUTINE VIEWGTLIST + +SUBROUTINE GETKRT(NP, KRT) + IMPLICIT NONE + INTEGER, INTENT(OUT) :: NP + CHARACTER(LEN=*), INTENT(OUT) :: KRT(*) + NP = 0 +END SUBROUTINE GETKRT + +SUBROUTINE GETKGT(NP, KGT) + IMPLICIT NONE + INTEGER, INTENT(OUT) :: NP + CHARACTER(LEN=*), INTENT(OUT) :: KGT(*) + NP = 0 +END SUBROUTINE GETKGT + +! ====================================================================== +! Section 4: ti-unique stubs (MOVE / TEXT / NUMBD) +! ====================================================================== +! ti/tigout.f90 references these on the print-page path. NUMBD also +! appears in tot/tot_graphics_stubs.f90 with the same signature; we +! keep this single definition. + +SUBROUTINE MOVE(X, Y) + IMPLICIT NONE + REAL, INTENT(IN) :: X, Y +END SUBROUTINE MOVE + +SUBROUTINE TEXT(STR, NW) + IMPLICIT NONE + CHARACTER(LEN=*), INTENT(IN) :: STR + INTEGER, INTENT(IN) :: NW +END SUBROUTINE TEXT + +SUBROUTINE NUMBD(VAL, FMT, NW) + IMPLICIT NONE + REAL, INTENT(IN) :: VAL + CHARACTER(LEN=*), INTENT(IN) :: FMT + INTEGER, INTENT(IN) :: NW +END SUBROUTINE NUMBD + +! ====================================================================== +! Section 5: wrx-unique stubs (GSAF subroutine surface) +! ====================================================================== +! wrx/wrview.f90 + wrx_dump_state.f90 + wrcalpwr.f90 reference these +! on the wave-plot diagnostic paths. None reached on the .so happy +! path (wrx_init -> wrx_set_param -> wrx_run -> wrx_get_state -> +! wrx_finalize). Arguments are intentionally typed as REAL / INTEGER +! with no INTENT(OUT) writes so call sites passing arbitrary types +! don't trip a write through a wrong-typed pointer. + +SUBROUTINE GSOPEN + IMPLICIT NONE +END SUBROUTINE GSOPEN + +SUBROUTINE GSCLOS + IMPLICIT NONE +END SUBROUTINE GSCLOS + +! Standalone GRD1D (wrx-only; distinct ELF symbol from +! grd1d_mod%GRD1D in §7 below) +SUBROUTINE GRD1D(IPAT, X, Y, NXM, NXMAX, NGMAX, TITLE, MODE_XY) + IMPLICIT NONE + INTEGER, INTENT(IN) :: IPAT, NXM, NXMAX, NGMAX, MODE_XY + REAL, INTENT(IN) :: X(*), Y(*) + CHARACTER(LEN=*), INTENT(IN) :: TITLE +END SUBROUTINE GRD1D + +SUBROUTINE R2W2B(FACTOR, RGB) + IMPLICIT NONE + REAL, INTENT(IN) :: FACTOR + REAL, INTENT(OUT) :: RGB(3) + RGB = 0.0 +END SUBROUTINE R2W2B + +SUBROUTINE W2G2B(FACTOR, RGB) + IMPLICIT NONE + REAL, INTENT(IN) :: FACTOR + REAL, INTENT(OUT) :: RGB(3) + RGB = 0.0 +END SUBROUTINE W2G2B + +SUBROUTINE R2Y2W(FACTOR, RGB) + IMPLICIT NONE + REAL, INTENT(IN) :: FACTOR + REAL, INTENT(OUT) :: RGB(3) + RGB = 0.0 +END SUBROUTINE R2Y2W + +SUBROUTINE GMNMX1(A, N, M, XMIN, XMAX) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*) + INTEGER, INTENT(IN) :: N, M + REAL, INTENT(OUT) :: XMIN, XMAX + XMIN = 0.0; XMAX = 0.0 +END SUBROUTINE GMNMX1 + +SUBROUTINE GMNMX2(A, NX, NY, NXM, M1, M2, XMIN, XMAX) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*) + INTEGER, INTENT(IN) :: NX, NY, NXM, M1, M2 + REAL, INTENT(OUT) :: XMIN, XMAX + XMIN = 0.0; XMAX = 0.0 +END SUBROUTINE GMNMX2 + +SUBROUTINE GQSCAL(XMIN, XMAX, GXMIN, GXMAX, GXSTEP) + IMPLICIT NONE + REAL, INTENT(IN) :: XMIN, XMAX + REAL, INTENT(OUT) :: GXMIN, GXMAX, GXSTEP + GXMIN = 0.0; GXMAX = 0.0; GXSTEP = 0.0 +END SUBROUTINE GQSCAL + +SUBROUTINE SETLIN(A, B, C) + IMPLICIT NONE + INTEGER, INTENT(IN) :: A, B, C +END SUBROUTINE SETLIN + +SUBROUTINE SETCHS(A, B) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B +END SUBROUTINE SETCHS + +SUBROUTINE SETFNT(A) + IMPLICIT NONE + INTEGER, INTENT(IN) :: A +END SUBROUTINE SETFNT + +SUBROUTINE SETRGB(R, G, B) + IMPLICIT NONE + REAL, INTENT(IN) :: R, G, B +END SUBROUTINE SETRGB + +SUBROUTINE SETCLP(A, B, C, D) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D +END SUBROUTINE SETCLP + +SUBROUTINE SETLNW(A) + IMPLICIT NONE + REAL, INTENT(IN) :: A +END SUBROUTINE SETLNW + +SUBROUTINE SETMKS(A, B) + IMPLICIT NONE + INTEGER, INTENT(IN) :: A + REAL, INTENT(IN) :: B +END SUBROUTINE SETMKS + +SUBROUTINE GDEFIN(A, B, C, D, E, F, G, H) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D, E, F, G, H +END SUBROUTINE GDEFIN + +SUBROUTINE GFRAME + IMPLICIT NONE +END SUBROUTINE GFRAME + +SUBROUTINE GSCALE(A, B, C, D, E, F) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D, E + INTEGER, INTENT(IN) :: F +END SUBROUTINE GSCALE + +SUBROUTINE GSCALL(A, B, C, D, E, F) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D, E + INTEGER, INTENT(IN) :: F +END SUBROUTINE GSCALL + +SUBROUTINE GVALUE(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GVALUE + +SUBROUTINE GVALUL(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GVALUL + +SUBROUTINE GPLOTP(X, Y, N, A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: X(*), Y(*) + INTEGER, INTENT(IN) :: N, A, B, C, D, E +END SUBROUTINE GPLOTP + +SUBROUTINE OFFCLP(A) + IMPLICIT NONE + INTEGER, INTENT(IN) :: A +END SUBROUTINE OFFCLP + +SUBROUTINE INQLNW(A) + IMPLICIT NONE + REAL, INTENT(OUT) :: A + A = 0.0 +END SUBROUTINE INQLNW + +SUBROUTINE INQTSZ(A) + IMPLICIT NONE + REAL, INTENT(OUT) :: A + A = 0.0 +END SUBROUTINE INQTSZ + +INTEGER FUNCTION NGULEN(X) + IMPLICIT NONE + REAL, INTENT(IN) :: X + NGULEN = 0 +END FUNCTION NGULEN + +! 3D plot stubs (wrx-only) +SUBROUTINE GDEFIN3D(A, B, C, D, E, F, G) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D, E, F, G +END SUBROUTINE GDEFIN3D + +SUBROUTINE GAXIS3D(A, B) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B +END SUBROUTINE GAXIS3D + +SUBROUTINE GSCALE3DX(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GSCALE3DX + +SUBROUTINE GSCALE3DY(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GSCALE3DY + +SUBROUTINE GSCALE3DZ(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GSCALE3DZ + +SUBROUTINE GVALUE3DX(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GVALUE3DX + +SUBROUTINE GVALUE3DY(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GVALUE3DY + +SUBROUTINE GVALUE3DZ(A, B, C, D, E) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D + INTEGER, INTENT(IN) :: E +END SUBROUTINE GVALUE3DZ + +SUBROUTINE GVIEW3D(A, B, C, D, E, F, G) + IMPLICIT NONE + REAL, INTENT(IN) :: A, B, C, D, E, F, G +END SUBROUTINE GVIEW3D + +! Contour stubs (wrx-only) +SUBROUTINE CONTF2(A, B, C, D, E, F, G, H) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E + INTEGER, INTENT(IN) :: F, G, H +END SUBROUTINE CONTF2 + +SUBROUTINE CONTF3(A, B, C, D, E, F, G, H, I) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F + INTEGER, INTENT(IN) :: G, H, I +END SUBROUTINE CONTF3 + +SUBROUTINE CONTF4(A, B, C, D, E, F, G, H, I, J) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F, G + INTEGER, INTENT(IN) :: H, I, J +END SUBROUTINE CONTF4 + +SUBROUTINE CONTG2(A, B, C, D, E, F, G, H) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E + INTEGER, INTENT(IN) :: F, G, H +END SUBROUTINE CONTG2 + +SUBROUTINE CONTG3(A, B, C, D, E, F, G, H, I) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F + INTEGER, INTENT(IN) :: G, H, I +END SUBROUTINE CONTG3 + +SUBROUTINE CONTG4(A, B, C, D, E, F, G, H, I, J) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F, G + INTEGER, INTENT(IN) :: H, I, J +END SUBROUTINE CONTG4 + +SUBROUTINE CONTQ2(A, B, C, D, E, F, G, H) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E + INTEGER, INTENT(IN) :: F, G, H +END SUBROUTINE CONTQ2 + +SUBROUTINE CONTQ3(A, B, C, D, E, F, G, H, I) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F + INTEGER, INTENT(IN) :: G, H, I +END SUBROUTINE CONTQ3 + +SUBROUTINE CONTQ3D1(A, B, C, D, E, F, G, H, I, J) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F, G + INTEGER, INTENT(IN) :: H, I, J +END SUBROUTINE CONTQ3D1 + +SUBROUTINE CONTQ4(A, B, C, D, E, F, G, H, I, J) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F, G + INTEGER, INTENT(IN) :: H, I, J +END SUBROUTINE CONTQ4 + +SUBROUTINE CPLOT3D1(A, B, C, D, E, F, G, H, I) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F + INTEGER, INTENT(IN) :: G, H, I +END SUBROUTINE CPLOT3D1 + +SUBROUTINE GDATA3D1(A, B, C, D, E, F, G, H, I) + IMPLICIT NONE + REAL, INTENT(IN) :: A(*), B(*), C(*), D, E, F + INTEGER, INTENT(IN) :: G, H, I +END SUBROUTINE GDATA3D1 + +! ====================================================================== +! Section 6: grd1d_mod (fp + ti) +! ====================================================================== + +MODULE grd1d_mod +CONTAINS + SUBROUTINE GRD1D(NGP, FX, FY, NXM, NXMAX, NLMAX) + IMPLICIT NONE + INTEGER, INTENT(IN) :: NGP, NXM, NXMAX, NLMAX + REAL, INTENT(IN) :: FX(*), FY(*) + END SUBROUTINE GRD1D +END MODULE grd1d_mod + +! ====================================================================== +! Section 7: grd2d_mod (eq + tr + fp + ti) +! ====================================================================== + +MODULE grd2d_mod +CONTAINS + SUBROUTINE GRD2D(NGP, FX, FY, FF, NXM, NXMAX, NYMAX) + IMPLICIT NONE + INTEGER, INTENT(IN) :: NGP, NXM, NXMAX, NYMAX + REAL, INTENT(IN) :: FX(*), FY(*), FF(*) + END SUBROUTINE GRD2D +END MODULE grd2d_mod