v0.0.5: Quantities, satisfaction verdicts and RDF round trip
Highlights
Requirements and constraints actually decide something. A requirement or constraint condition is evaluated against the features of the element stating it — its own attributes, the ones it inherits from its definition, and the values a usage rebinds (attribute :>> maxVerticalSpeed = 1.5;). This was the first known limitation listed for 0.0.4. assert satisfy <requirement> by <part>; now has a verdict of its own: the requirement's subject is bound to an object of the part named by by, and %satisfy evaluates the satisfaction assertions a model states — every one, or the ones a named element states. Assertions can be negated (assert not constraint { … }), a violated condition reports which condition failed, and a requirement with no condition has no verdict rather than passing vacuously.
Quantities and units. attribute maxVerticalSpeed = 1.5 [m/s]; is evaluated as a real quantity: units reduce to a scale factor over base units through the Quantities and Units library's own unitConversion, so commensurable units convert before a comparison or a sum — 1.5 [m/s] <= 5.4 [km/h] is true, exactly, at its boundary — and a composed unit is kept (10 [m] / 2 [s] is 5 [m/s], 4 [m] / 2 [m] is 2). Comparing incommensurable units is an error rather than a comparison of bare magnitudes. Units may be written unqualified at the prompt, a name a declaration shadows is reported with the shadowing declaration and the way out (… shadowing the measurement unit SI::metre — write SI::m to name the unit), %calc accepts quantity arguments in every argument form, and a quantity renders like the bare Real it measures (v = -15.20 [m/s]) instead of full float precision.
Math functions. The KerML function library's scalar numerics are evaluable (sqrt, abs, floor, round, max, min, isZero, isUnit, and the trigonometric family), as is exponentiation (**, ^). exp, ln, log(x, base) and atan2(y, x) have no OMG signature, so they live in a new non-normative extension library, SystemicaMathFunctions.kerml (import SystemicaMathFunctions::*;) — the vendored OMG files are unchanged and the stdlib parse gate is 95/95 clean. A result that is not a finite value of the declared type is reported where it is evaluated instead of folding to a NaN, an infinity or a wrapped integer.
Action and state execution. while, loop, for … in … and if … else … lower to real decision and merge nodes and execute, so an iterating body reaches its final node with the values it computed rather than deadlocking. A then written as a body member is a real succession edge, like the standalone form. An attribute default written in an action or state body is evaluated in the scope that declares it, so a unit or type an enclosing package imports resolves there.
Saving and RDF interop. %save <file> writes the session out — notation (.sysml) or RDF Turtle (.ttl), chosen by the extension, written atomically. A session that does not fully parse still saves as notation, with the syntax errors reported as warnings, so work is never trapped in the REPL. sysml -convert converts between notation and Turtle in both directions, round-tripping packages, definitions, usages, features, imports, connectors, successions and satisfy assertions; what the mapping normalizes and what it refuses is documented in docs/RDF_INTEROP.md.
Runaway bounds you can raise. The evaluation, action-step, event and do-activity budgets are each configurable (SYSML_MAX_STEPS, SYSML_MAX_ACTION_STEPS, SYSML_MAX_EVENTS, SYSML_MAX_DO_STEPS), defaults raised to 10M/1M/1M/5M, and every limit error names the variable that raises it. The evaluation budget bounds one run rather than a whole session, so a long REPL session no longer exhausts its allowance and starts failing everything.
Editor and tooling correctness. A relationship written with a keyword and one written with its symbol are the same relationship end to end (specializes/:>, subsets/:>, redefines/:>>, references/::>), so hover, go-to-definition, completion and the index agree whichever spelling a model uses; a feature taking its effective name from what it redefines is read under that name too. Removing a document unwinds what its wildcard re-exports contributed and reuses its index slot, so an editing session's memory no longer climbs with the number of reindexes. A parser try-parse that gives up now rewinds properly, fixing a condition beginning with a feature named constraint.
Installation
Homebrew (macOS and Linux, avoids the Gatekeeper quarantine a browser download sets):
brew install Open-MBEE/tap/systemicaOr download the bundle for your platform from the assets below and verify it:
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.5/systemica-linux-amd64.tar.gz
curl -fLO https://github.com/Open-MBEE/Systemica/releases/download/v0.0.5/SHA256SUMS.txt
sha256sum -c SHA256SUMS.txt --ignore-missing
tar xzf systemica-linux-amd64.tar.gz && ./sysml --versionAssets
Per platform (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64):
sysml-<os>-<arch>andsysml-lsp-<os>-<arch>archives (.tar.gz,.zipon Windows);systemica-<os>-<arch>bundle archives holding both binaries under their plain names, which is the layout Homebrew and a PATH install expect;sysml-grpc-<os>-<arch>, raw with a.sha256sidecar — new in this release. This is what the Python client downloads, verifies, caches under~/.pysysml/binand starts, so a Python caller needs no Go toolchain. v0.0.4 described these assets but did not carry them; 0.0.5 is the first release that does;SHA256SUMS.txtover every archive and everysysml-grpcbinary.
Python client
pysysml is released independently, by its own pysysml-v* tag, because it resolves a sysml-grpc binary at runtime from whichever release the caller names rather than from a release matching its own version. Until that tag is pushed, install it from source:
pip install "git+https://github.com/Open-MBEE/Systemica.git#subdirectory=python"Python 3.10+. See docs/RELEASING.md.
Known limitations
- RDF/Turtle conversion has no mapping for a member that states a condition or a step, so a model containing one is reported rather than converted:
requireandassumemembers, a constraint body's condition,subject, a computedreturn,assign,if/while/loop/for, substates, transitions andentry/do/exit(cannot convert the *ast.RequireMember at <file>:<line>). A requirement stating a condition, and any state machine or action body with statements, must be saved as.sysml. Full list in docs/RDF_INTEROP.md. - The REPL's prompt evaluates in the last namespace the session declared. After typing a second package, the first package's members and the units its imports brought in are reached by qualified name only (
1.0 [SI::m], not1.0 [m]). - Re-typing a declaration whose name the session already holds replaces the earlier snippet rather than merging into it, so adding a member to a package by re-typing the package drops the members left out of the new text.
- A multi-valued feature that is both typed and given a default takes the typed instantiation; the default is not merged into it (as in 0.0.4).
- An attribute declared with a type but no value (
attribute diameter : Real;) instantiates as an object of that type rather than an unset value, so%slotsshowsdiameter = Instance(ID: n)with(no features)under it. - The macOS and Windows binaries are unsigned, so a browser download is quarantined by Gatekeeper or flagged by SmartScreen. Install with Homebrew or
curl; see docs/MACOS_DISTRIBUTION.md for the workarounds and what signing would take.
Verification
Measured on the commit this release is cut from: gofmt clean, go build ./... and go vet ./... clean, make lint (staticcheck + gosec) passing, go test -race -count=1 ./... → 2,465 tests and subtests, 2,460 pass / 5 self-skip / 0 fail, stdlib conformance 95/95 clean, OMG training corpus 98/100 clean (the two remaining files hold four pinned OMG source errors), Python client 92 passed / 18 skipped.