Skip to content

Discrete variables support in GEMSEO - #92

Open
FrancoisGallard wants to merge 21 commits into
MDO-Standards:developfrom
FrancoisGallard:gemseo-disc
Open

FrancoisGallard wants to merge 21 commits into
MDO-Standards:developfrom
FrancoisGallard:gemseo-disc

Conversation

@FrancoisGallard

Copy link
Copy Markdown

Add discrete variables support in PhiloteDiscipline and GEMSEOtoPhiloteDiscipline

FrancoisGallard and others added 21 commits September 17, 2026 15:27
Add philote_mdo.gemseo, providing PhiloteDiscipline (a GEMSEO
Discipline that calls a remote Philote-MDO server) and
GEMSEOtoPhiloteDiscipline (the opposite direction, serving a GEMSEO
discipline over gRPC). Add matching examples (Paraboloid,
OpenAeroStruct, and the Sellar problem driven from OpenMDAO) and
integration tests under tests/, adapted to the current
OpenMdaoSubProblem() + add_group() construction pattern. Also add the
gemseo extra to pyproject.toml, add gemseo to the CI test dependencies,
and fix proto compilation on Windows.
Add a "Working with GEMSEO" section with two tutorials, ported from
the retired Jupyter Book docs: using PhiloteDiscipline to optimize a
remote Paraboloid discipline, and coupling OpenAeroStruct with GEMSEO
through an OpenMDAO sub-problem. Enable @docusaurus/theme-mermaid so
the flowchart diagrams on these pages render.

Note: docs/package-lock.json was not regenerated (no Node/npm
available in this environment) -- run `npm install` in docs/ before
the next `npm ci` build.
Covers the ValueError on a missing channel, sending discipline options
to the server, the ndarray-sized default_output_data branch in
GEMSEOtoPhiloteDiscipline.setup(), and the defensive skip of Jacobian
entries for outputs outside a wrapped discipline's own output grammar.
Also marks the TYPE_CHECKING-only imports as pragma: no cover, since
they can never execute at runtime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nder

The GEMSEO tutorials use ```mermaid fences, but the theme was never
actually wired in: package.json didn't list the dependency and
docusaurus.config.ts had no markdown.mermaid / themes entry, so the
diagrams were not compiled and rendered as plain code blocks instead.

Verified by building the site and loading both tutorial pages in a
browser: the flowcharts now render as SVG diagrams.

Note: docs/package-lock.json still needs to be regenerated with real
npm (this environment only has bun) before `npm ci` in CI will pick up
the new dependency.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The 0.8.0 versioned docs describe the released package, which has no
philote_mdo.gemseo module. The GEMSEO pages stay under docs/docs/ (the
Next version) and will be snapshotted by the release workflow with the
first release that ships them.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
package.json gained @docusaurus/theme-mermaid without a matching lock
update, so the documentation workflow's npm ci refused to install.
Regenerated with npm 10 (the version CI's Node 20 ships) in
lockfile-only mode: this adds mermaid and its dependencies, and bumps
katex from 0.16.45 to 0.16.47 because mermaid requires ^0.16.47.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
philote-examples 0.5.1 is the first release whose OasAerostructDiscipline
declares the CD and CL partials with respect to alpha. With earlier
versions the SLSQP scenario fails on its first gradient request.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
PhiloteDiscipline is built on ExplicitClient, so it connects to explicit
Philote-MDO servers only; against an implicit server it fails at execute
with "Method not found". The intro also named RemoteExplicitComponent,
the OpenMDAO client, as the way to serve an OpenMDAO model; serving is
done with OpenMdaoSubProblem.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
The GEMSEO tests import gemseo at module level, so without it installed
they fail to import. List it next to OpenMDAO in the README and the
installation page.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
Fix the misspelled GEMSEOtoPhiloteDiscipline attribute and constructor
argument before they are released as public API.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
The constructor documented that an empty name would use the remote
discipline's name, but passed it straight to GEMSEO, so every remote
discipline was named PhiloteDiscipline. Query GetInfo and use the
server-reported name when none is given; GEMSEO still falls back to the
class name when the server reports none (the default).

Dynamic-shape and discrete server variables were not handled. Dynamic
shapes were never sent, so the first execute failed with an error asking
for SetVariableShapes, which a GEMSEO user cannot call. Discrete
variables were left out of the grammars and silently kept their
server-side defaults. Reject both at construction with a
NotImplementedError naming the variables, until they are supported.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
test_sellar_mda_compute only asserted that obj and con1 were present, so
any values passed. Evaluate at the canonical Sellar starting point
(x = 1, z = [5, 2]) and compare against the canonical result there.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
Python 3.9 reached end of life in October 2025, and GEMSEO 6.3 requires
3.10, so the 3.9 CI job resolved GEMSEO 6.2 while every other job tested
6.3. Require Python >=3.10, remove 3.9 from the CI matrix and the
classifiers, and update the agent guidelines and the landing page.

Signed-off-by: Christopher Lupp <christopherlupp@gmail.com>
PhiloteDiscipline rejected any server that declared discrete variables.
They are now part of the GEMSEO grammars next to the continuous ones,
bound to no type, since a Philote discrete variable carries a
google.protobuf.Value and may hold any JSON-compatible value.

Discrete inputs are split off the GEMSEO input data and sent on their own
side of the wire protocol, for both ComputeFunction and ComputeGradient,
and the discrete outputs returned by the server are merged back into the
output data.

Discrete variables are excluded from the Jacobian. GEMSEO already filters
non-continuous variables out when the differentiated variables are chosen
with add_differentiated_inputs/outputs, but not when the whole Jacobian is
requested, so _get_differentiated_io is overridden to do it there too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every variable of the wrapped GEMSEO discipline was served as a flat
continuous Philote variable. The names of the input and output grammars
are now classified with their data converter's is_numeric(): a numeric
name is declared as a continuous Philote variable, any other name as a
discrete one, which carries any JSON-compatible value.

The discrete inputs sent by the client are merged back into the GEMSEO
input data before execute() and linearize(), and the discrete outputs of
the wrapped discipline are streamed back to the client.

Only the variables holding continuous data take part in the Jacobian, so
that no partial derivative is declared that compute_partials() would then
leave at zero. This also drops the integer-valued variables, which are
numeric but not differentiable. compute_partials() therefore selects the
differentiated variables with add_differentiated_inputs/outputs, which
apply that same filter, rather than asking for the whole Jacobian.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add a "Discrete variables" section to the GEMSEO page covering both
directions: how GEMSEOtoPhiloteDiscipline classifies a grammar name with
its data converter's is_numeric(), and how PhiloteDiscipline puts the
server's discrete variables in the GEMSEO grammars. Note that they are
never differentiated, and that Philote-MDO does not transfer their
default values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two branches of GEMSEOtoPhiloteDiscipline were unreached: skipping an
integer-valued output in setup_partials(), and the early return of
compute_partials() when nothing was declared to differentiate. Add a
discipline with integer variables and one whose variables are all
non-numeric, which brings philote_mdo.gemseo to 100% statement and
branch coverage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…umeric

A continuous Philote variable travels as an array of doubles, and the
DiscreteVariable message carries a google.protobuf.Value whose documented
contents include integers. An integer-valued variable therefore belongs to
the discrete side of the protocol, as it does in OpenMDAO, whose bindings
map Philote discrete variables onto add_discrete_input/output.

is_numeric differs from is_continuous by exactly the int type, so it sent
integers down the double path, where they were silently converted to floats
and came back as floats.

This also removes the need for the second predicate: setup_partials() had to
re-filter its declarations with is_continuous so as not to declare a partial
derivative that compute_partials() would leave at zero. The classification
and the Jacobian now agree by construction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@chrislupp chrislupp added the enhancement New feature or request label Sep 19, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants