Querying SciFMs Hosted by Nomad¶
This notebook walks through a complete Nomad inference workflow against a local MCP endpoint launched from this repository’s container/deploy/demo configuration.
You will start the server with five MIST models and the Janus HEAT PLI model, discover the SciFMs it exposes, inspect a model card, and run a PDE surrogate that returns a full solution field.
By the end, you will have confirmed connectivity to the local endpoint, fetched model metadata, and compared an input state with the model’s predicted final state.
The first server startup can take a while because
uvmay need to resolve the environment forcontainer/deploy/demo. You also need access to the public model sources referenced bycontainer/deploy/demo/nomad.yml.
Launch the Nomad Server¶
This notebook starts nomad serve in the background using the demo server project under container/deploy/demo.
That project includes a public demo config containing five MIST models and the Janus HEAT PLI model.
The hidden cells below define the notebook-specific startup helpers, and the visible cell after them launches the server.
You need uv installed because the launcher uses uv run --directory container/deploy/demo ....
SERVER_DIR = "container/deploy/demo"
def get_client():
"""Function for connecting to a local Nomad server
Update to point to a hosted one, by just returning a
StreamableHttpTransport(url=nomad_mcp_server_url, headers=...)
"""
from fastmcp.client.transports import StreamableHttpTransport
server = require_nomad_server(SERVER_DIR)
return StreamableHttpTransport(url=server.url)
start_nomad_server(SERVER_DIR)
List Available Tools¶
This is the fastest way to confirm that the local endpoint is reachable and that the Nomad tools are visible through MCP. If you want more detail on the transport layer itself, see the Code-Mode Gateway reference and the MCP documentation.
async def list_tools(name_filter: str | None = None) -> list[str]:
# Ask the local server for the exposed tools, then optionally narrow the list.
async with Client(get_client()) as client:
tools = await client.list_tools()
names = sorted(tool.name for tool in tools)
if name_filter is None:
return names
return [name for name in names if name_filter in name]
tool_names = await list_tools()
model_card_tool_name = "get_model_card"
mist_qm9_tool_name = "mist_models---mist_26p9M_kkgx0omx_qm9"
heat_pli_tool_name = "diffunet2_heat_pli_f2l"
missing_tool_names = [
name
for name in (model_card_tool_name, mist_qm9_tool_name, heat_pli_tool_name)
if name not in tool_names
]
if missing_tool_names:
raise LookupError(
f"Expected tools were not exposed: {missing_tool_names}.\n"
f"Available tools: {tool_names}"
)
{
"model_card_tool_name": model_card_tool_name,
"mist_qm9_tool_name": mist_qm9_tool_name,
"heat_pli_tool_name": heat_pli_tool_name,
"tool_names": tool_names,
}
Fetch a Model Card¶
Nomad provides a get_model_card(tool_name: str) tool for retrieving the model card, when one is available, for each SciFM it serves.
The model card is the README.md in the SciFM’s model directory.
It is the quickest way to inspect a deployed model’s metadata, expected inputs, and intended behavior before running inference.
If you want the builder-side view of how SciFMs are served by Nomad, the Model Builders guide is the next place to read. You can use the template model card as a starting point for documenting your own model.
model_card = await get_model_card(tool_name=model_card_tool_name)
display_model_card(
model_card=model_card,
max_height="28rem",
)
Run Inference on a PDE SciFM¶
Now that we are connected to a Nomad endpoint, let’s query a PDE surrogate trained on the HEAT PLI dataset. These models take an initial state as an AutoRegressiveInput and return a predicted trajectory, or a final timestep, as a WellFormat.
We will start by loading an initial state with WellFormat.from_file, inspecting its metadata, and visualizing the input field before sending it to the local model.
import matplotlib.pyplot as plt
import numpy as np
from nomad.well_format import AutoRegressiveInput, WellFormat
INPUT_FILE = GUIDE_DIR / "angled_half_res.h5"
input_state = WellFormat.from_file(INPUT_FILE)
{
"input_file": str(INPUT_FILE.relative_to(REPO_ROOT)),
"dataset_name": input_state.dataset_name,
"grid_type": input_state.grid_type,
"n_spatial_dims": input_state.n_spatial_dims,
"field_count": len(input_state.t0_fields),
}
plot_density(
input_state=input_state,
field="av_density",
input_time_index=0, # Change this to look at different timesteps of the simulation
);
Run Local Inference¶
We can now query the locally hosted SciFM to predict the final state of the HEAT PLI simulation.
The request packages the initial condition into AutoRegressiveInput, sends it to the local tool, and parses the returned WellFormat response.
The final plot uses a shared color scale so you can compare the initial field with the predicted final state at a glance.
The same pattern works in scripts, larger pipelines, and builder-side evaluation loops; see Getting Started and the CLI reference for adjacent workflows.
prediction_state, elapsed_seconds = await run_inference(
initial_state=input_state,
duration=100,
)
print(f"Inference completed in {elapsed_seconds:.2f} seconds")
plot_density(
input_state=input_state,
predicted_state=prediction_state,
field="av_density",
input_time_index=0,
prediction_time_index=-1,
);
Troubleshooting¶
Missing
uv: installuv, then rerun the install and server-start cells.Kernel has no
pip: the setup cell usesuv pip --python <current-kernel>automatically, so rerun that cell after switching kernels.Startup failures:
start_nomad_server(...)returns the server log path; inspect it if the launch fails or times out.Changing the local deployment: edit
container/deploy/demo/nomad.yml, then rerun the server-start and tool-discovery cells.Stopping the background server: call
stop_nomad_server()if you want to shut it down before the kernel exits.Need more detail? See Getting Started, the Well Format reference, the Code-Mode Gateway reference, the CLI reference, and the Model Builders guide.