Solver output streams — transcript, events, and monitors#
Python client examples for the Transcript, Events, and Monitor
gRPC services.
See Transcript, Events, and Monitor for these services’ complete reference material.
import grpc
from ansys.api.fluent.v1 import (
events_pb2, events_pb2_grpc,
monitor_pb2, monitor_pb2_grpc,
transcript_pb2, transcript_pb2_grpc,
)
channel = grpc.insecure_channel("<server-address>")
metadata = [("password", "<password>")]
transcript_stub = transcript_pb2_grpc.TranscriptStub(channel)
events_stub = events_pb2_grpc.EventsStub(channel)
monitor_stub = monitor_pb2_grpc.MonitorStub(channel)
Opening a transcript stream#
Transcript.BeginStreaming opens a server-streaming call; each response
carries one line (or chunk) of Fluent console output in resp.transcript.
stream = transcript_stub.BeginStreaming(
transcript_pb2.TranscriptRequest(),
metadata=metadata,
)
print(stream is not None) # -> True
stream.cancel()
Opening two independent transcript streams#
Multiple simultaneous BeginStreaming calls return independent stream
objects and can be cancelled separately.
stream1 = transcript_stub.BeginStreaming(
transcript_pb2.TranscriptRequest(), metadata=metadata
)
stream2 = transcript_stub.BeginStreaming(
transcript_pb2.TranscriptRequest(), metadata=metadata
)
print(stream1 is not stream2) # -> True
stream1.cancel()
stream2.cancel()
Cancelling a transcript stream#
Cancelling a stream and then iterating it raises grpc.StatusCode.CANCELLED
or StopIteration — both are normal.
stream = transcript_stub.BeginStreaming(
transcript_pb2.TranscriptRequest(),
metadata=metadata,
)
stream.cancel()
try:
next(iter(stream))
except grpc.RpcError as e:
print(e.code() == grpc.StatusCode.CANCELLED) # -> True
except StopIteration:
pass # also acceptable
Opening an event stream#
Events.BeginStreaming opens a server-streaming call; each response carries
exactly one event in its oneof as union.
stream = events_stub.BeginStreaming(
events_pb2.BeginStreamingRequest(),
metadata=metadata,
)
print(stream is not None) # -> True
stream.cancel()
Decoding event types#
Call WhichOneof("as") on each response to identify the event and then
access its payload fields.
valid_event_fields = {
field.name
for field in events_pb2.BeginStreamingResponse.DESCRIPTOR.oneofs_by_name["as"].fields
}
stream = events_stub.BeginStreaming(
events_pb2.BeginStreamingRequest(),
metadata=metadata,
)
for event_response in stream:
kind = event_response.WhichOneof("as")
if kind is not None:
print(kind in valid_event_fields) # -> True
if kind == "iteration_ended_event":
print(event_response.iteration_ended_event.index) # -> 1
elif kind == "progress_event":
progress_event_data = event_response.progress_event
print(progress_event_data.percent_complete, progress_event_data.message) # -> 12.5 'Solving...'
elif kind == "solver_time_estimate_event":
time_estimate_event = event_response.solver_time_estimate_event
print(time_estimate_event.hours, time_estimate_event.minutes, time_estimate_event.seconds) # -> 0 2 34.5
elif kind == "error_event":
print(event_response.error_event.message) # -> 'Diverged'
break
stream.cancel()
Registering a pause trigger#
PauseSolveFor registers a pause that fires after each iteration or time
step; it returns a unique registration_id.
pause_registration = events_stub.PauseSolveFor(
events_pb2.PauseSolveForRequest(
solution_event=events_pb2.SOLUTION_EVENT_ITERATION
),
metadata=metadata,
)
print(isinstance(pause_registration.registration_id, int)) # -> True
print(pause_registration.registration_id > 0) # -> True
# Register a second trigger — IDs must be distinct.
second_pause_registration = events_stub.PauseSolveFor(
events_pb2.PauseSolveForRequest(
solution_event=events_pb2.SOLUTION_EVENT_ITERATION
),
metadata=metadata,
)
print(pause_registration.registration_id != second_pause_registration.registration_id) # -> True
Registering a time-step pause trigger#
SOLUTION_EVENT_TIME_STEP fires at the end of each time step in a
transient simulation.
timestep_pause_registration = events_stub.PauseSolveFor(
events_pb2.PauseSolveForRequest(
solution_event=events_pb2.SOLUTION_EVENT_TIME_STEP
),
metadata=metadata,
)
print(timestep_pause_registration.registration_id > 0) # -> True
Cancelling a pause registration#
CancelPauseSolve removes a previously registered pause trigger.
cancel_response = events_stub.CancelPauseSolve(
events_pb2.CancelPauseSolveRequest(
registration_id=pause_registration.registration_id
),
metadata=metadata,
)
print(cancel_response is not None) # -> True
# Clean up the other registrations.
events_stub.CancelPauseSolve(
events_pb2.CancelPauseSolveRequest(registration_id=second_pause_registration.registration_id),
metadata=metadata,
)
events_stub.CancelPauseSolve(
events_pb2.CancelPauseSolveRequest(registration_id=timestep_pause_registration.registration_id),
metadata=metadata,
)
Resuming the solver#
ResumeSolve unpauses a session that was suspended by a PauseSolveFor
registration; passing an unknown ID is accepted without raising an error.
events_stub.ResumeSolve(
events_pb2.ResumeSolveRequest(registration_id=999999999),
metadata=metadata,
)
Discovering monitor sets#
GetMonitors enumerates every monitor set registered with the solver,
including its name, type, x-axis type, update frequency, and unit metadata.
monitors_response = monitor_stub.GetMonitors(
monitor_pb2.GetMonitorsRequest(),
metadata=metadata,
)
for ms in monitors_response.monitor_sets:
print(ms.name) # -> 'residuals'
print(ms.monitors) # -> ['continuity', 'x-velocity', 'energy']
print(ms.frequency) # -> 1
print(monitor_pb2.MonitorType.Name(ms.type)) # -> MONITOR_TYPE_RESIDUAL
print(monitor_pb2.XAxisType.Name(ms.axis)) # -> XAXIS_TYPE_ITERATION
Validating monitor set metadata#
Every monitor set must have a non-empty name, at least one monitor entry,
and a non-negative frequency; unit_info.factor is non-negative when set.
# Two consecutive calls must return identical monitor set names.
first_monitors_response = monitor_stub.GetMonitors(
monitor_pb2.GetMonitorsRequest(), metadata=metadata
)
second_monitors_response = monitor_stub.GetMonitors(
monitor_pb2.GetMonitorsRequest(), metadata=metadata
)
names1 = sorted(ms.name for ms in first_monitors_response.monitor_sets)
names2 = sorted(ms.name for ms in second_monitors_response.monitor_sets)
print(names1 == names2) # -> True
for ms in first_monitors_response.monitor_sets:
print(len(ms.name) > 0) # -> True
print(len(ms.monitors) > 0) # -> True
print(ms.frequency >= 0) # -> True
if ms.unit_info.unit:
print(ms.unit_info.factor >= 0) # -> True
Opening a monitor stream#
Monitor.BeginStreaming streams live y-axis samples; each response contains
an XAxisData point and one or more MonitorData entries.
stream = monitor_stub.BeginStreaming(
monitor_pb2.StreamingRequest(),
metadata=metadata,
)
print(stream is not None) # -> True
stream.cancel()
Reading monitor samples#
Each StreamingResponse exposes x_axis_data (index and type) and
y_axis_values (name + float value per monitored quantity).
stream = monitor_stub.BeginStreaming(
monitor_pb2.StreamingRequest(),
metadata=metadata,
)
sample = next(iter(stream))
stream.cancel()
print(hasattr(sample, "x_axis_data")) # -> True
print(monitor_pb2.XAxisType.Name(sample.x_axis_data.x_axis_type)) # -> XAXIS_TYPE_ITERATION
for y in sample.y_axis_values:
print(len(y.name) > 0) # -> True
print(isinstance(y.value, float)) # -> True
print(f"{y.name} = {y.value:.4e}")
# -> continuity = 9.8765e-03
# -> x-velocity = 4.3210e-04
# -> energy = 1.2345e-06
Filtering the monitor stream#
Pass a MonitorFilter with x_axis_type to receive only iteration- or
time-based samples.
stream = monitor_stub.BeginStreaming(
monitor_pb2.StreamingRequest(
filters=[
monitor_pb2.MonitorFilter(
x_axis_type=monitor_pb2.XAXIS_TYPE_ITERATION
)
]
),
metadata=metadata,
)
print(stream is not None) # -> True
stream.cancel()
Cancelling a monitor stream#
Cancelling a monitor stream and then iterating it raises
grpc.StatusCode.CANCELLED or StopIteration — both are normal.
stream = monitor_stub.BeginStreaming(
monitor_pb2.StreamingRequest(),
metadata=metadata,
)
stream.cancel()
try:
next(iter(stream))
except grpc.RpcError as e:
print(e.code() == grpc.StatusCode.CANCELLED) # -> True
except StopIteration:
pass # also acceptable
See Transcript, Events, and Monitor for the complete reference material.