Repository navigation
Conversation
fp_param_set takes NRMAX, NPMAX, NTHMAX, LMAXFP and the species maps as they come, and fp_run then met values the solver cannot run with only deep inside fp_prep or fp_loop: an array index out of bounds (an abort when the library is built with run-time checks, undefined without them), a process that ends, a call that never returns, or a run whose results fp_get_state cannot return. fp_param_check (fp/fp_param_registry.f90) checks them together and fp_api_run calls it before fp_prep (validation in one batch before the run, k-yoshimi#143, instead of a STOP reached from the library, k-yoshimi#142). A violation is FP_ERR_INVALID and one "XX fp_run: ..." line on unit 6; the handle stays usable, so a corrected count runs on the next call. NRMAX 1 .. FP_MAX_NRMAX (the state layout, passed in by fp_api) NPMAX, NTHMAX 2 or more LMAXFP 0 .. HUGE-1. At HUGE the iteration's exit at convergence, N_IMPL = 1 + LMAXFP (fp/fploop.f90), overflows and N_IMPL <= LMAXFP stays true. Below 0 no pass is made and fp_loop tests an error flag that no pass has set NSAMAX at most NSBMAX (the collision arrays are NSBMAX wide in both species directions) NS_NSA(1..NSAMAX) 1 .. MIN(NSMAX, NSBMAX) NS_NSB(1..NSBMAX) 1 .. NSMAX A 0 in either map is the slot's own number, as fp_set_nsa_nsb substitutes it. NSBMAX above NSMAX is not refused in itself. One of these is a change for a run that used to complete: with NRMAX above FP_MAX_NRMAX the run was made, fp_get_state returned rc=3, and the results could be read only from unit 6 or fp_regress.dat (FP_REGRESS_DUMP=1). It is now refused before the run (CHANGELOG: Changed). Measured on libfpapi.so (macOS arm64, gfortran, -O0 with run-time checks; one process per case, 10 x 10 momentum cells, fp_run(1) unless stated): case before after NRMAX = 101 run; get_state rc=3 rc=1 NRMAX = 101, fp_run(0) accepted; get_state rc=3 rc=1 NRMAX = 0, -1 abort (index of rm) rc=1 NRMAX = 0, fp_run(0) abort (index of rm) rc=1 NPMAX = 1, 0; NTHMAX = 1, 0 abort rc=1 LMAXFP = -1 "XX mtx_abort", exit rc=1 LMAXFP = 2147483647 no return in 120 s rc=1 NSAMAX=2, NSBMAX=1, NS_NSA(2)=1 abort (2 of lnlam) rc=1 NS_NSA(1)=3, NSMAX=3, NSBMAX=2 abort (3 of fnsb) rc=1 NS_NSA(1)=3, NSMAX=2, NSBMAX=3, NS_NSB={1,2,1} abort (3 of plf) rc=1 NS_NSB(2)=3, NSMAX=2, NSBMAX=2 abort (3 of plf) rc=1 NSBMAX=3, NSMAX=2 (slot 3: species 3) abort (3 of plf) rc=1 NS_NSA(1) = -1 abort (-1 of modelw) rc=1 NS_NSB(2) = -1, NSMAX = NSBMAX = 2 abort (-1 of pz) rc=1 NS_NSA(2) = 0 (so 2), NSMAX = 1, NSAMAX = NSBMAX = 2, NS_NSB(2) = 1 abort (2 of plf) rc=1 NS_NSB(3) = 0 (so 3), NSMAX = 2, NSBMAX = 3 abort (3 of plf) rc=1 NRMAX = 100 runs, state returned same NPMAX = 2; NTHMAX = 2 runs same LMAXFP = 0, 2147483646 runs same NSAMAX = NSBMAX = 2 runs same NS_NSA(1) = 2, NSBMAX = 2 runs same NSMAX=2, NSBMAX=3, NS_NSB={1,2,1} runs same NS_NSA(2) = NS_NSB(2) = 0, NSMAX = NSAMAX = NSBMAX = 2 runs same NS_NSA = {1,1}, NS_NSB = {1,1}, NSMAX = 1, NSAMAX = NSBMAX = 2 runs same NS_NSB(1) = 2, NSBMAX = 1 (species 1 in no background slot) runs, with a message same The namelist path's fp_check (fp/fpparm.f90) stays empty: the menu calls it after every input line, where a rule between two parameters would refuse the state between two lines. Not checked, and said so in docs/fp-library/architecture.md: a mesh too large for memory or for a default integer; NPMAX or NTHMAX of 2 (it runs and is too coarse for a result); an evolved species that no background slot names (reported by fp_set_nsa_nsb, and the run is made). fp_get_state after a refusal reads the last run's arrays with the counts now set, past their end when a count was raised, as after any fp_set_param: it is not to be called before the next run that is made. Tests: python/fplib/tests/test_run_checks.py, 34 tests, one per case and no subTest (under --forked a failing subTest is reported as passed); each species case breaks one rule only. The runs that must still be made take three steps and assert the time reached, the extents asked for and a finite positive density (the two-cell runs, whose density is wrong, the time only). The LMAXFP = HUGE and HUGE-1 cases run in a process of their own, killed if it does not end: a forked test cannot be stopped from outside a Fortran loop. One test holds the eight message lines of a run that breaks every rule. 28 mutations of the check and of its call (each bound moved or removed, the zero substitution removed or changed, a loop shortened, a message dropped, the call disabled), rebuilt one by one: the tests notice every one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HengyuLi-Ozaki-lab
added a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 5, 2026
…-run-checks-proposed docs(product-line): group I is upstream pull request k-yoshimi#242; what group J waits for, measured
k-yoshimi
approved these changes
Oct 13, 2026
k-yoshimi
left a comment
Owner
There was a problem hiding this comment.
Reviewed at 3eb8f8c4 (based on current develop tip). Approving.
Verified locally (macOS arm64, gfortran 15, repo make.header, pytest 9.1.1 --forked --timeout=120 --timeout-method=signal, with the strict-subTest override from #243 active):
- Rebuilt
fp/libfpapi.sofrom the PR head (fp_param_checkexported) and ranpython/fplib+python/mcp-servers/fp_mcp: 119 passed, 3 skipped (the Linux-canonical equivalence cases). All 36 cases in the newtest_run_checks.pypass, including the two process-isolatedLMAXFPcases. - Cross-checked the bounds against the sources they cite: the collision arrays are
(…, NSBMAX, NSBMAX)(fp/fpcomm.f90:472-474),FNSBis written at the species number (fp/fpmpi.f90:65-71),fp_loop'sDO WHILE (N_IMPL .le. LMAXFP)withN_IMPL = 1 + LMAXFPas the convergence exit,FP_MAX_NRMAX = 100in bothfp/fp_state.f90andpython/fplib/_ffi.py. The registry's set-time caps (NSMAX <= 100,NSAMAX/NSBMAX <= 8) keep the loops'MIN(…, SIZE(NS_NSA))guards from ever truncating. - The check sits after the
g_initialized/ntmax < 0guards and beforefp_prep, so a refused handle stays usable, asTestARefusalLeavesTheLibraryUsableshows.
Scope note for the record: the Fortran change is confined to the C-ABI entry (fp_api.f90) and the registry module, no solver change.
Non-blocking:
CHANGELOG.mddescribes the same change under both "Changed" and "Fixed"; one entry (Fixed, with theNRMAX > FP_MAX_NRMAXbehaviour change called out inside it) would read cleaner.- The refusal reasons go to unit 6 only. Through
fp_mcpthey are currently shredded into the JSON-RPC stream (the problem #232 addresses), so an LLM client sees onlyinvalid parameteruntil #232 lands. Not this PR's concern, just the ordering: #232 (after itsmain()rework) makes this PR's diagnostics visible.
Owner
|
Independent Codex review (second reviewer, read-only) found no HIGH items; approval stands. For the record:
|
Owner
|
@cursor review |
Bugbot couldn't runBugbot is not enabled for your user on this team. Ask your team administrator to increase your team's hard limit for Bugbot seats or add you to the allowlist in the Cursor dashboard. |
This was referenced Oct 13, 2026
This was referenced Oct 13, 2026
HengyuLi-Ozaki-lab
pushed a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 14, 2026
…i#245) The FPCOMM profile arrays (RNT, RWT, RTT, RJT, RPCT, RPWT) keep the extents of the last fp_prep, while NRMAX / NSAMAX are the current namelist values. fp_set_param on either count clears g_prepared without reallocating, so until the next fp_run the two disagree and fp_get_state copied nsa = 1..NSAMAX, nr = 1..NRMAX out of smaller arrays: a read past the allocation (silent in the -O3 library build, an abort under -fcheck=all). k-yoshimi#242's early refusal in fp_run added one more way to be left in that window. fp_get_state now copies MIN(NRMAX, SIZE(RNT,1)) x MIN(NSAMAX, SIZE(RNT,2)) (and ntg_last bounded by SIZE(RNT,3)) and reports the copied extents in state%nrmax / state%nsamax, so the [nsamax][nrmax] slice is always data that exists: a lowered count truncates the snapshot at once, a raised one stays capped at the old allocation until the next run. Behaviour before the first run in a process (nothing allocated: namelist counts, zero profiles) and the rc=3 refusal above FP_MAX_NRMAX are unchanged. Tests: python/fplib/tests/test_state_extents.py (9 cases, no subTest): NSAMAX / NRMAX raised without a run (the two reads past the arrays), NRMAX / NSAMAX lowered, NRMAX raised then run (the bound follows the reallocation), NRMAX above the cap still rc=3, fp_run(0), a second handle in the same process reading before its first run (per-process arrays, counts reset by fp_init: a 1 x 1 slice of the old data), and the pre-first-run snapshot; all six profile arrays are checked. The assertions are on the reported extents and copied values, so they hold with or without run-time bounds checking. Verified once with fp_api.o compiled with DFLAGS (-fcheck=all): develop's fp_api.f90 fails the two raised-count cases, this one passes. Docs: docs/fp-library/architecture.md (the "do not call fp_get_state in that window" note from k-yoshimi#242 replaced by the new contract, incl. the per-process allocation) and the fp state.md tables (en/ja). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TA2KahTs3qLMZL6yjSsWwT
HengyuLi-Ozaki-lab
pushed a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 14, 2026
…-yoshimi#189) tr_tst2 + NSMAX=3 went NaN (WPT/BETA*/TAUE*) after one step. The issue blamed the equilibrium import copying RNU/RTU(...,3) from an eqdata built for two species; the tst2 fixture runs MDLUF=0, where tr_prof builds RN/RT for ns = 1..NSMAX from the namelist pairs PN/PNS and PT/PTS and RNU/RTU are unused. Species 3 simply keeps the tr_init defaults PN(3)=PNS(3)=0, so its density is identically zero and the transport step divides by it (every species' RT goes NaN). Reproduced on the fixture: PN[3]/PNS[3] set -> finite (even 1e-8), PN[3]=0 with PNS[3]>0 -> finite, PN[3]<0 -> NaN, PT[3]=0 or PTS[3]=0 -> NaN. NSMAX=2 with PN[2]=PNS[2]=0 failed inside tr_loop (ierr=3) instead. tr/tr_api.f90: private species_profile_violations (MDLUF=0, ns = 1..MIN(NSMAX, array extents)): negative PN/PNS -> OUT_OF_RANGE, both zero -> OUT_OF_RANGE_AFTER_DEP, PT<=0 / PTS<=0 -> OUT_OF_RANGE, named in the registry spelling (PN[3]). tr_api_run calls it whenever it is about to run tr_prep and returns TR_ERR_INVALID with one "XX tr_run: PN[3]: ..." line per finding on unit 6 (the fp_run contract from k-yoshimi#242; the handle stays usable). tr_api_validate pushes the same findings as diagnostics. C-ABI layer only; trprof.f90 untouched. The tr_init defaults (NSMAX=2) and every fixture / sample input (iter01, m0904: NSMAX=4 with four positive values each) pass unchanged. Tests: test_NSMAX_in_range loses its strict xfail (the refusal is the tolerated controlled failure; k-yoshimi#241's strict subTest keeps the NaN visible should it return); a positive test runs NSMAX=3/4 finite with the added species' profiles; python/trlib/tests/test_species_profile_ checks.py (16 cases, one per case): refusal matrix, accepted cases (incl. PN=0 with PNS>0 and handle reuse after a refusal) and validate() parity. Docs: python/trlib/README.md, docs/sphinx/modules/tr/{en,ja}/ parameter-setting.md (what validate() checks). Local (macOS, CI flags): trlib + tr_mcp + totlib 265 passed, 1 pre-existing py3.10 ExceptionGroup failure, 18 pre-existing skips; `make -C tr tr_api_check_so` OK (the non-PIC tr_api_check_all link needs the gsaf graphics library, absent on macOS; CI runs it). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TA2KahTs3qLMZL6yjSsWwT
HengyuLi-Ozaki-lab
added a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 14, 2026
develop gained k-yoshimi#242, k-yoshimi#243, k-yoshimi#248, k-yoshimi#249, k-yoshimi#251, k-yoshimi#254 and k-yoshimi#255 since this branch left it. k-yoshimi#248 (fp_get_state bounds its copies by the allocated extents) touches the same declaration line of fp_api_get_state as this branch. One conflict, fp/fp_api.f90: the local variables of fp_api_get_state. Both sides added names to the same INTEGER statement; the merge keeps all of them (ntg1_last from this branch, nr_alloc and nsa_alloc from k-yoshimi#248). The two bodies do not overlap: k-yoshimi#248's copy of the six profiles is bounded by the allocated extents, and the global scalars of this branch were already bounded by SIZE(PIT, 1) and SIZE(PIT, 2). In the state k-yoshimi#248 is about (a count changed by fp_set_param after a run) the scalar block is not executed at all: fp_set_param clears g_prepared, and the scalars read zero, as this branch's documentation says. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HengyuLi-Ozaki-lab
added a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 14, 2026
After the merge of develop, fp_get_state follows two rules in the window between an fp_set_param and the next run: the six profiles are still the last run's, within the allocated extents (k-yoshimi#245, k-yoshimi#248), and the seven global scalars read zero, because they are filled from a prepared state only. Nothing tested the second in that window; a change of the gate would have left the suite green. python/fplib/tests/test_state_scalars_window.py, 8 cases: the state has the seven; zero before the first run; the volume and the stored energy after a run, and after a run of zero steps; after a set_param of NSAMAX + 1, NRMAX + 1 and NRMAX - 1 the seven are zero while timefp and the profiles are the last run's within the extents k-yoshimi#245 gives (3 x 1, 3 x 1, 2 x 1); the next run fills them again. With the gate changed to "the arrays are allocated" the three window cases and the last one fail (measured). The documents that said "zero until the first run" now also say "and again after a set_param until the next run that is made, while the profiles are still those of the last run" (architecture.md, state.md en and ja, the fplib README, fp_api.h, state.py, the fp_mcp schema text, a comment in fp_api.f90). "That is made": since k-yoshimi#242 an fp_run can be refused before it prepares. CHANGELOG.md gets the entry this pull request did not have. No code change: the comment in fp_api.f90 is the only Fortran line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HengyuLi-Ozaki-lab
added a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 18, 2026
develop gained k-yoshimi#242, k-yoshimi#243, k-yoshimi#248, k-yoshimi#249, k-yoshimi#251, k-yoshimi#254, k-yoshimi#255, k-yoshimi#256, k-yoshimi#257, k-yoshimi#258, k-yoshimi#259, k-yoshimi#261, k-yoshimi#263 and k-yoshimi#264 since this branch left it. One conflict, in python/eqlib/_ffi.py, python/fplib/_ffi.py and python/trlib/_ffi.py alike: the end of load_library. k-yoshimi#261 (k-yoshimi#246) checks the state layout of the library just loaded and raises <Mod>libAbiError on a mismatch; this branch closes the directory it registered with os.add_dll_directory when the load fails. The check is now inside the block that cleans up, so a library refused for its layout does not leave its directory in the process-wide DLL search set either (or an entry in _DLL_DIR_HANDLES). Order as on develop: the layout check, then the prototypes. With the check on the load path, the cases of test_platform_libname.py that fake the loaded library (ctypes.CDLL patched) were refused by it: 24 of 39 failed. Each of the three files now has an autouse fixture that patches check_state_layout out for them (the check has its own cases in test_ffi.py, TestLibraryLayout), and one new case per module puts a check back that raises AbiMismatchError (with the prototypes passing, so that only the check can refuse the load) and asserts <Mod>libAbiError, the directory handle closed and nothing registered; with the check moved after the cleanup block, that case fails (measured on eq). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HengyuLi-Ozaki-lab
added a commit
to HengyuLi-Ozaki-lab/task
that referenced
this pull request
Oct 21, 2026
develop now carries the pieces split out of this branch (k-yoshimi#256 eq_save, k-yoshimi#257 the MCP fd isolation, k-yoshimi#265 the mcp pin) and k-yoshimi#232, k-yoshimi#233, k-yoshimi#240, k-yoshimi#242, k-yoshimi#243, k-yoshimi#248, k-yoshimi#249, k-yoshimi#251, k-yoshimi#254, k-yoshimi#255, k-yoshimi#258, k-yoshimi#259, k-yoshimi#261, k-yoshimi#263, k-yoshimi#264. 43 files conflicted; each was resolved as follows. - Taken from develop (36 files): every file where this branch only changed the mcp pin to "mcp>=0.9,<2" (develop has ">=1.2,<2", k-yoshimi#265): the seven pyproject.toml, six READMEs, the fp, ti, tot, wr and wrx servers, the Sphinx MCP pages, the trlib presentation and its builder, the 2026-04-18 MCP plan; and the eq_save pieces now in develop in their reviewed form: eq/eq_api.f90, eq/eq_api.h, python/eqlib/tests/test_save.py, eq_mcp/tests/test_save_tool.py; and tr_mcp/server.py, whose changes here were this branch's own fd isolation (develop's _fd_isolation replaces it) and the os._exit(0) at the end of main(). - The os._exit(0) that ended main() of tr_mcp and eq_mcp on this branch (to skip the Python teardown, cited in k-yoshimi#227 item 5) is gone with them: develop's main() returns 0, as approved in k-yoshimi#257. The abort it avoided was a double DEALLOCATE, which this branch fixed in tr/trcomm.f90 (DEALLOCATE_TRCOMM returns when nothing is allocated); that fix stays, merged without a conflict. - eq/eqfile.f90: develop's (EQSAVE without an argument, EQSAVE_IERR doing the work, and its kept EQDSKR error), plus this branch's MODELG=25 in EQ_READ's EQDSK branch (k-yoshimi#228-2). eq/eqmenu.f90 merged without a conflict but kept this branch's "CALL EQSAVE(IERR)"; it is develop's "CALL EQSAVE" again, as EQSAVE takes no argument. - python/eqlib/_ffi.py, eqlib.py, tests/test_eqlib.py: develop's, plus this branch's get_psi_rz (the eq_common_get_psi_rz_ prototype, the Eq.get_psi_rz method, its closed-handle assertion). - eq_mcp/server.py: develop's (the save tool of k-yoshimi#256, the shared fd isolation), plus this branch's get_psi_rz handler and tool and its --print-tools entry (13 tools). eq_mcp/tests/test_server.py: this branch's (13 tools with save and get_psi_rz), which contains develop's change. - python/mcp-servers/eq_mcp/tests/test_import_isolation.py (this branch only, no conflict) is removed: it pinned this branch's own isolation helper (_install_fd_isolation, _FD_ISOLATION_INSTALLED in eq_mcp and tr_mcp), which develop's python/_fd_isolation.py replaces; develop's tests/test_fd_isolation.py of each of the seven servers checks the same contract (an import leaves fd 1 and sys.stdout alone). - .github/workflows/python-tests.yml: develop's mcp pin; this branch's numpy install (the get_psi_rz tests import numpy) and ../task symlink step kept. - CHANGELOG.md did not conflict. The three k-yoshimi#228 entries this branch held back in d8b6e4d are added to [Unreleased] Fixed, after its first entry, where no other open pull request inserts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
fp_runrefuses, before any work, the counts the FP solver cannot run with.fp_param_check(
fp/fp_param_registry.f90) holds the rules in one place andfp_api_runcalls it beforefp_prep. A violation isFP_ERR_INVALIDwith oneXX fp_run: ...line per violation on unit 6; the handle stays usable, so a corrected countruns on the next call.
developNRMAXFP_MAX_NRMAX(100)fp_get_statereturns rc=3NPMAX,NTHMAXLMAXFPHUGE − 1fp_looptests an error flag that no pass has set (fp/fploop.f90:231; on the build measured the process ends); atHUGEthe iteration's exit at convergence,N_IMPL = 1 + LMAXFP(fp/fploop.f90:98), overflows,N_IMPL <= LMAXFP(:81) stays true and the step never endsNSAMAXNSBMAXNSBMAXwide in both species directions (fp/fpcomm.f90:472-474)NS_NSA(1..NSAMAX)MIN(NSMAX, NSBMAX)NSMAX, and its distribution is copied into the background array at its species number (fp/fpmpi.f90:65-71)NS_NSB(1..NSBMAX)NSMAXNSMAXA 0 in either map counts as the slot's own number, which
fp_set_nsa_nsbsubstitutes when the run is prepared.NSBMAXaboveNSMAXis not refused in itself: with everyNS_NSBentry at mostNSMAXit runs (measured, below).FP_MAX_NRMAXis the API's limit, sofp_apipasses it in; the registry module does not depend onfp_state.fp_get_state's own check stays.Why
fp_param_settakes these counts as they come, and ondevelopa value the solver cannot run with is met only deepinside
fp_preporfp_loop: an array index out of bounds (an abort when the library is built with run-time checks,undefined behaviour in the default
-O3build), a process that ends, a call that never returns, or a run whose resultsfp_get_statecannot return. For a host that holds the library in its own process (the MCP server, a Python session)each of these takes the host with it. This is validation in one batch before the run (#143) instead of a
STOPreachedfrom the library (#142).
Found while documenting FP's parameters for a downstream client (TASK Web Client); in use there since 2026-10-03.
Measured on
libfpapi.so(macOS arm64, gfortran 15,-O0with run-time checks; one process per case, 10 × 10momentum cells,
fp_run(1)unless stated):developNRMAX = 101fp_get_staterc=3fp_runrc=1NRMAX = 101withfp_run(0)fp_get_staterc=3NRMAX = 0,-1;NRMAX = 0withfp_run(0)rm)NPMAX = 1,0;NTHMAX = 1,0LMAXFP = -1XX mtx_abort: ierr_g= -1, the process endsLMAXFP = 2147483647NSAMAX = 2,NSBMAX = 1,NS_NSA(2) = 1lnlam)NS_NSA(1) = 3,NSMAX = 3,NSBMAX = 2fnsb)NS_NSA(1) = 3,NSMAX = 2,NSBMAX = 3,NS_NSB = {1, 2, 1}plf)NS_NSB(2) = 3,NSMAX = 2,NSBMAX = 2plf)NSBMAX = 3,NSMAX = 2(slot 3 is species 3)plf)NS_NSA(1) = -1modelw)NS_NSB(2) = -1,NSMAX = NSBMAX = 2pz)NS_NSA(2) = 0(so 2),NSMAX = 1,NSAMAX = NSBMAX = 2,NS_NSB(2) = 1plf)NS_NSB(3) = 0(so 3),NSMAX = 2,NSBMAX = 3plf)NRMAX = 100NPMAX = 2;NTHMAX = 2LMAXFP = 0,2147483646NSAMAX = NSBMAX = 2; species 2 evolved alone withNSBMAX = 2NSMAX = 2,NSBMAX = 3,NS_NSB = {1, 2, 1}NSMAX = 1)NS_NSB(1) = 2,NSBMAX = 1(species 1 in no background slot)XX NS_NSA has no correponding NS_NSBChecked
pytest python/ --forked --timeout=120 --timeout-method=signal), the sevenlib*api.soandlibtotapi_mono.sobuilt,MONO_LIB_PATHunset; macOS (Apple silicon), gfortran 15.2.0(Homebrew,
-O0 -fbounds-check -fcheck=all), Python 3.12.13, pytest 9.0.3, mcp 1.27.1, bpsddevelopfb8414f:develop4a5d977 and this one commit):757 passed, 35 skipped, 1 xpassed;developalone, the same build:723 passed, 35 skipped, 1 xpassed. The 34 more are the new tests.Linux-canonical"), the mono tests that need
MONO_LIB_PATH(CI's mono job runs them) and threeti_mcptestswithout their data fixture. They were not run here: upstream's Linux CI is their verdict. Downstream, a Linux CI
with the same four jobs has run them green on this commit merged with other changes, not on this branch alone. The
xpass is
trlib'stest_NSMAX_in_range(tr_tst2 fixture: NSMAX>2 mutation produces NaN scalars (RNU/RTU species 3+ unpopulated) #189), as ondevelop.python/fplib/tests/test_run_checks.py, 34 tests, one per case and nosubTest(pytest --forked reports a failing unittest subTest as passed (24 test files use subTest) #241), each speciescase breaking one rule only. The runs that must still be made take three steps and assert the time reached
(
3 × DELTwith aDELTthat is not the default), the extents asked for and a finite, positive density (thetwo-cell runs, whose density is wrong, the time only). One test holds the eight
XX fp_run:lines of a run thatbreaks every rule. Against
develop's library the refusals fail (the table above: an abort of the forked child, arun that is made, a session that ends, no return within the time limit) and the runs that must still be made pass.
map, each map's loop shortened, a message line removed, the call disabled, a larger limit passed), the library
rebuilt for each. The tests notice every one.
make -C fp fp_api_check_so: OK.LMAXFP = HUGEandHUGE − 1cases run in a process of their own (subprocess.run(..., timeout=30)), killed ifit does not end: a forked test that hangs inside a Fortran loop cannot be stopped from outside (pytest-timeout ends
the session with an internal error and the child keeps running).
Things to know
NRMAXabove 100 the run was made,fp_get_statereturned rc=3, and the results could be read only from unit 6 or from
fp_regress.dat(FP_REGRESS_DUMP=1). It isnow refused before the run;
CHANGELOG.mdlists it under "Changed". Every other rule is a bound the solver indexesby.
fpprogram is unchanged. The namelist path'sfp_check(fp/fpparm.f90) stays empty: the menucalls it after every input line, where a rule between two parameters (
NSAMAX <= NSBMAX) would refuse the statebetween two lines.
fp_runreturnsFP_ERR_INVALID; the reason is theXX fp_run:line on unit 6. Afp_validate()that returns the list, aseq_validatedoes and Parameter validation API: pre-run consistency check across modules #143 proposes for every module, could be built onfp_param_check.docs/fp-library/architecture.md:NPMAX = 2orNTHMAX = 2(it runs and is toocoarse for a result); a mesh too large for memory, or whose
NRMAX × NPMAX × NTHMAXpasses a default integer(
fp/fpprep.f90:597); an evolved species that no background slot names (reported byfp_set_nsa_nsb, and the runis made, without collisions with its own kind).
fp_get_stateafter a refusal reads the last run's arrays with the counts now set, past their end when a countwas raised, as it does on
developafter anyfp_set_paramwithout a run. The document now says not to call itbefore the next run that is made; the fix belongs to
fp_api_get_stateand to a pull request of its own.fp/fp_api.h, the header comment offp/fp_api.f90andfp_mcp's tool text do not mention the check, so thatthis branch merges with feat(fp_mcp): expose set_param_str tool for KNAMEQ / MODELG=3 equilibrium load #231, fix(fp_mcp): redirect Fortran WRITE(6,...) to stderr to protect JSON-RPC channel #232 and feat(fp): expose global plasma scalars (Ip, stored energy, powers, volume) in fp_state_t #233 without a hand (they change the lines next to them).
python/fplib/fplib.py(run) anddocs/fp-library/architecture.mddocument it. Say the word and the three textsare added here, or in a follow-up once those are merged.
fp_api.f90), 28 ofdocumentation, 21 of CHANGELOG, 7 of a docstring.
Scope
Based on current
develop(4a5d977) and standing alone on it: one commit. Independent of #226, #231, #232, #233 and#240: it merges with each of their heads without a conflict (
git merge-tree); it shares one file with #233 only(
fp/fp_api.f90, different hunks) and none with the others.🤖 Generated with Claude Code