Now we need to change the simulation tasks to use the partitions we've created. There are two steps to this process:
First, the field space declarations will need to change to accomodate the new partitions. Remember that the nodes have been partitioned three ways: into private, shared, and ghost subregions (example reproduced below). This means that a task which wants to operate for example on the red nodes will need three regions (private, shared, and ghost). This also means that any fspace which points to nodes must be updated to take three arguments as well.
Private (Partition of Nodes)
|
Shared (Partition of Nodes)
|
Ghost (Partition of Nodes)
|
After this is done, the rest is straightforward: change each task call in the main simulation loop to operate over subregions of the partitions. (Hint: Use a for loop.)
In [ ]:
ptr(T, R1, R2, R3) -- Pointer types may point to multiple regions.
P[I] -- Returns the Ith subregion of the partition P.
In [ ]:
import "regent"
local c = regentlib.c
struct Currents {
_0 : float,
_1 : float,
_2 : float,
}
struct Voltages {
_1 : float,
_2 : float,
}
fspace Node {
capacitance : float,
leakage : float,
charge : float,
voltage : float,
}
-- TODO: Change Wire to take three arguments: private, shared, and
-- ghost nodes.
fspace Wire(rn : region(Node)) {
in_node : ptr(Node, rn),
out_node : ptr(Node, rn),
inductance : float,
resistance : float,
capacitance : float,
current : Currents,
voltage : Voltages,
}
local CktConfig = require("session1/circuit_config")
local helper = require("session2/circuit_helper")
local validator = require("session2/circuit_validator")
local WS = 3
local dT = 1e-7
-- TODO: Change this task to take three regions of nodes (private,
-- shared, and ghost).
task calculate_new_currents(steps : uint,
rn : region(Node),
rw : region(Wire(rn)))
where
reads(rn.voltage,
rw.{in_node, out_node, inductance, resistance, capacitance}),
reads writes(rw.{current, voltage})
do
var rdT : float = 1.0 / dT
__demand(__vectorize)
for w in rw do
var temp_v : float[WS + 1]
var temp_i : float[WS]
var old_i : float[WS]
var old_v : float[WS - 1]
temp_i[0] = w.current._0
temp_i[1] = w.current._1
temp_i[2] = w.current._2
for i = 0, WS do old_i[i] = temp_i[i] end
temp_v[1] = w.voltage._1
temp_v[2] = w.voltage._2
for i = 0, WS - 1 do old_v[i] = temp_v[i + 1] end
-- Pin the outer voltages to the node voltages.
temp_v[0] = w.in_node.voltage
temp_v[WS] = w.out_node.voltage
-- Solve the RLC model iteratively.
var L : float = w.inductance
var rR : float = 1.0 / w.resistance
var rC : float = 1.0 / w.capacitance
for s = 1, steps + 1 do
-- First, figure out the new current from the voltage differential
-- and our inductance:
-- dV = R*I + L*I' ==> I = (dV - L*I')/R
for i = 0, WS do
temp_i[i] = ((temp_v[i + 1] - temp_v[i]) -
(L * (temp_i[i] - old_i[i]) * rdT)) * rR
end
-- Now update the inter-node voltages.
for i = 0, WS - 1 do
temp_v[i + 1] = old_v[i] + dT * (temp_i[i] - temp_i[i + 1]) * rC
end
end
-- Write out the results.
w.current._0 = temp_i[0]
w.current._1 = temp_i[1]
w.current._2 = temp_i[2]
w.voltage._1 = temp_v[1]
w.voltage._2 = temp_v[2]
end
end
-- TODO: Change this task to take three regions of nodes (private,
-- shared, and ghost).
task distribute_charge(rn : region(Node),
rw : region(Wire(rn)))
where
reads(rw.{in_node, out_node, current._0, current._2}),
reduces +(rn.charge)
do
for w in rw do
var in_current = -dT * w.current._0
var out_current = dT * w.current._2
w.in_node.charge += in_current
w.out_node.charge += out_current
end
end
task update_voltages(rn : region(Node))
where
reads(rn.{capacitance, leakage}),
reads writes(rn.{voltage, charge})
do
for n in rn do
var voltage = n.voltage + n.charge / n.capacitance
voltage = voltage * (1.0 - n.leakage)
n.voltage = voltage
n.charge = 0.0
end
end
task toplevel()
var conf : CktConfig
conf:initialize_from_command()
conf:show()
var num_circuit_nodes = conf.num_pieces * conf.nodes_per_piece
var num_circuit_wires = conf.num_pieces * conf.wires_per_piece
var rn = region(ispace(ptr, num_circuit_nodes), Node)
var rw = region(ispace(ptr, num_circuit_wires), Wire(wild))
new(ptr(Node, rn), num_circuit_nodes)
new(ptr(Wire(wild), rw), num_circuit_wires)
c.printf("Generating a random circuit...\n")
helper.generate_random_circuit(rn, rw, conf)
var colors = ispace(int1d, conf.num_pieces)
var pn_equal = partition(equal, rn, colors)
var pw = preimage(rw, pn_equal, rw.in_node)
var pn_extrefs = image(rn, preimage(rw, pn_equal, rw.out_node) - pw, rw.out_node)
var pn_private = pn_equal - pn_extrefs
var pn_shared = pn_equal & pn_extrefs
var pn_ghost = image(rn, pw, rw.out_node) - pn_equal
helper.dump_graph(conf, rn, rw)
for i = 0, conf.num_pieces do
helper.initialize_pointers(pn_private[i], pn_shared[i], pn_ghost[i], pw[i])
end
helper.wait_for(helper.block(rn, rw))
c.printf("Starting main simulation loop\n")
var ts_start = helper.timestamp()
for j = 0, conf.num_loops do
-- TODO: Change each of these calls to work on a piece of the
-- graph.
calculate_new_currents(conf.steps, rn, rw)
distribute_charge(rn, rw)
update_voltages(rn)
end
-- Wait for all previous tasks to complete and measure the elapsed time.
var _ = 0
for i = 0, conf.num_pieces do
_ += helper.block(pn_equal[i], pw[i])
end
helper.wait_for(_)
var ts_end = helper.timestamp()
c.printf("simulation complete\n")
var sim_time = 1e-6 * (ts_end - ts_start)
c.printf("ELAPSED TIME = %7.3f s\n", sim_time)
var gflops =
helper.calculate_gflops(sim_time, WS * 6 + (WS - 1) * 4, 4, 4, conf)
c.printf("GFLOPS = %7.3f GFLOPS\n", gflops)
c.printf("Validating simulation results...\n")
validator.validate_solution(rn, rw, conf)
end
regentlib.start(toplevel)
That's it, you've finished the simulation! Hope you've enjoyed the exercise.
Note: There is another session which covers (mapping the computation onto GPUs). However, this machine is not equipped with GPUs, so you won't be able to follow the instructions and see it run. Feel free to take a look at play around with it, if you're still interested.