Adapting visualization with Triggers¶
Triggers allow the user to specify a set of actions that are triggered by the result of a boolean expression. They provide flexibility to adapt what analysis and visualization actions are taken in situ. Triggers leverage Ascent’s Query and Expression infrastructure. See Ascent’s Triggers docs for deeper details on Triggers.
Using triggers to render when events occur¶
#include <iostream>
#include <sstream>
#include "ascent.hpp"
#include "conduit_blueprint.hpp"
#include "ascent_tutorial_cpp_utils.hpp"
using namespace ascent;
using namespace conduit;
int main(int argc, char **argv)
{
// Use triggers to render when conditions occur
Ascent a;
// open ascent
a.open();
// setup actions
Node actions;
// declare a question to ask
Node &add_queries = actions.append();
add_queries["action"] = "add_queries";
// add our entropy query (q1)
Node &queries = add_queries["queries"];
queries["q1/params/expression"] = "entropy(histogram(field('gyre'), num_bins=128))";
queries["q1/params/name"] = "entropy";
// declare triggers
Node &add_triggers = actions.append();
add_triggers["action"] = "add_triggers";
Node &triggers = add_triggers["triggers"];
// add a simple trigger (t1_ that fires at cycle 500
triggers["t1/params/condition"] = "cycle() == 500";
triggers["t1/params/actions_file"] = "cycle_trigger_actions.yaml";
// add trigger (t2) that fires when the change in entroy exceeds 0.5
// the history function allows you to access query results of previous
// cycles. relative_index indicates how far back in history to look.
// Looking at the plot of gyre entropy in the previous notebook, we see a jump
// in entropy at cycle 200, so we expect the trigger to fire at cycle 200
triggers["t2/params/condition"] = "entropy - history(entropy, relative_index = 1) > 0.5";
triggers["t2/params/actions_file"] = "entropy_trigger_actions.yaml";
// print our full actions tree
std::cout << actions.to_yaml() << std::endl;
// gyre time varying params
int nsteps = 10;
float time_value = 0.0;
float delta_time = 0.5;
Node mesh;
for( int step =0; step < nsteps; step++)
{
// call helper that generates a gyre time varying example mesh.
// gyre ref :https://shaddenlab.berkeley.edu/uploads/LCS-tutorial/examples.html
tutorial_gyre_example(time_value, mesh);
// update the example cycle
int cycle = 100 + step * 100;
mesh["state/cycle"] = cycle;
std::cout << "time: " << time_value << " cycle: " << cycle << std::endl;
// publish mesh to ascent
a.publish(mesh);
// execute the actions
a.execute(actions);
// update time
time_value = time_value + delta_time;
}
// retrieve the info node that contains the query results
Node info;
a.info(info);
// close ascent
a.close();
// this will render:
// cycle_trigger_out_500.png
// entropy_trigger_out_200.png
//
//
// We can also examine when the triggers executed by looking at the expressions
// results in the output info
//
std::cout << info["expressions"].to_yaml() << std::endl;
}
import conduit
import conduit.blueprint
import ascent
import numpy as np
from ascent_tutorial_py_utils import tutorial_gyre_example
# Use triggers to render when conditions occur
a = ascent.Ascent()
a.open()
# setup actions
actions = conduit.Node()
# declare a question to ask
add_queries = actions.append()
add_queries["action"] = "add_queries"
# add our entropy query (q1)
queries = add_queries["queries"]
queries["q1/params/expression"] = "entropy(histogram(field('gyre'), num_bins=128))"
queries["q1/params/name"] = "entropy"
# declare triggers
add_triggers = actions.append()
add_triggers["action"] = "add_triggers"
triggers = add_triggers["triggers"]
# add a simple trigger (t1_ that fires at cycle 500
triggers["t1/params/condition"] = "cycle() == 500"
triggers["t1/params/actions_file"] = "cycle_trigger_actions.yaml"
# add trigger (t2) that fires when the change in entroy exceeds 0.5
# the history function allows you to access query results of previous
# cycles. relative_index indicates how far back in history to look.
# Looking at the plot of gyre entropy in the previous notebook, we see a jump
# in entropy at cycle 200, so we expect the trigger to fire at cycle 200
triggers["t2/params/condition"] = "entropy - history(entropy, relative_index = 1) > 0.5"
triggers["t2/params/actions_file"] = "entropy_trigger_actions.yaml"
# view our full actions tree
print(actions.to_yaml())
# gyre time varying params
nsteps = 10
time = 0.0
delta_time = 0.5
for step in range(nsteps):
# call helper that generates a double gyre time varying example mesh.
# gyre ref :https://shaddenlab.berkeley.edu/uploads/LCS-tutorial/examples.html
mesh = tutorial_gyre_example(time)
# update the example cycle
cycle = 100 + step * 100
mesh["state/cycle"] = cycle
print("time: {} cycle: {}".format(time,cycle))
# publish mesh to ascent
a.publish(mesh)
# execute the actions
a.execute(actions)
# update time
time = time + delta_time
# retrieve the info node that contains the trigger and query results
info = conduit.Node()
a.info(info)
# close ascent
a.close()
# this will render:
# cycle_trigger_out_500.png
# entropy_trigger_out_200.png
#
# We can also examine when the triggers executed by looking at the expressions
# results in the output info
#
print(info["expressions"].to_yaml())
Output
-
action: "add_queries"
queries:
q1:
params:
expression: "entropy(histogram(field('gyre'), num_bins=128))"
name: "entropy"
-
action: "add_triggers"
triggers:
t1:
params:
condition: "cycle() == 500"
actions_file: "cycle_trigger_actions.yaml"
t2:
params:
condition: "entropy - history(entropy, relative_index = 1) > 0.5"
actions_file: "entropy_trigger_actions.yaml"
time: 0 cycle: 100
time: 0.5 cycle: 200
time: 1 cycle: 300
time: 1.5 cycle: 400
time: 2 cycle: 500
time: 2.5 cycle: 600
time: 3 cycle: 700
time: 3.5 cycle: 800
time: 4 cycle: 900
time: 4.5 cycle: 1000
entropy:
100:
value: 3.81580590726479
type: "double"
200:
value: 4.43027379899862
type: "double"
300:
value: 4.42357515605932
type: "double"
400:
value: 4.4133821818731
type: "double"
500:
value: 4.40290017527564
type: "double"
600:
value: 4.3643209637501
type: "double"
700:
value: 4.40290017527564
type: "double"
800:
value: 4.4133821818731
type: "double"
900:
value: 4.42357515605932
type: "double"
1000:
value: 4.43027379899862
type: "double"
cycle() == 500:
100:
value: 0
type: "bool"
200:
value: 0
type: "bool"
300:
value: 0
type: "bool"
400:
value: 0
type: "bool"
500:
value: 1
type: "bool"
600:
value: 0
type: "bool"
700:
value: 0
type: "bool"
800:
value: 0
type: "bool"
900:
value: 0
type: "bool"
1000:
value: 0
type: "bool"
entropy - history(entropy, relative_index = 1) > 0.5:
100:
value: 0
type: "bool"
200:
value: 1
type: "bool"
300:
value: 0
type: "bool"
400:
value: 0
type: "bool"
500:
value: 0
type: "bool"
600:
value: 0
type: "bool"
700:
value: 0
type: "bool"
800:
value: 0
type: "bool"
900:
value: 0
type: "bool"
1000:
value: 0
type: "bool"