Skip to content
Snippets Groups Projects
Commit dbf60bec authored by Udo Eisenbarth's avatar Udo Eisenbarth :speech_balloon:
Browse files

work on group node inversion

parent edd5f111
No related branches found
No related tags found
1 merge request!5Resolve "Implement "inverted" feature for group nodes"
Pipeline #7350 passed with warnings
use std::fs::File;
use std::io::Write;
use opossum::{
error::OpossumError,
lightdata::{DataEnergy, LightData},
nodes::{Detector, Dummy, NodeGroup, Source},
optic_node::OpticNode,
spectrum::create_he_ne_spectrum,
OpticScenery, analyzer::AnalyzerEnergy,
};
fn main() -> Result<(), OpossumError> {
let mut scenery = OpticScenery::new();
scenery.set_description("Inverse Group test".into());
let i_s = scenery.add_element(
"Source",
Source::new(LightData::Energy(DataEnergy {
spectrum: create_he_ne_spectrum(1.0),
})),
);
let mut group = NodeGroup::new();
group.expand_view(true);
let g_n1 = group.add_node(OpticNode::new("A", Dummy));
let g_n2 = group.add_node(OpticNode::new("B", Dummy));
group.connect_nodes(g_n1, "rear", g_n2, "front")?;
group.map_input_port(g_n1, "front", "in1")?;
group.map_output_port(g_n2, "rear", "out1")?;
let mut groupnode=OpticNode::new("group", group);
groupnode.set_inverted(true);
let i_g = scenery.add_node(groupnode);
let i_d = scenery.add_element("Detector", Detector::default());
scenery.connect_nodes(i_s, "out1", i_g, "out1")?;
scenery.connect_nodes(i_g, "in1", i_d, "in1")?;
let path = "group_reverse.dot";
let mut output = File::create(path).unwrap();
write!(output, "{}", scenery.to_dot()?).unwrap();
let mut analyzer = AnalyzerEnergy::new(&scenery);
analyzer.analyze()?;
scenery.report();
Ok(())
}
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
//! [`Light`] represents the information / data flowing from one node to another node. It contains information about //! [`Light`] represents the information / data flowing from one node to another node. It contains information about
//! the respective source an target port names this edge connects as well as the actual light information (stored as //! the respective source an target port names this edge connects as well as the actual light information (stored as
//! [`LightData`]). //! [`LightData`]).
use crate::lightdata::LightData; use crate::lightdata::LightData;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
...@@ -32,6 +33,11 @@ impl Light { ...@@ -32,6 +33,11 @@ impl Light {
pub fn set_data(&mut self, data: Option<LightData>) { pub fn set_data(&mut self, data: Option<LightData>) {
self.data = data; self.data = data;
} }
pub fn inverse(&mut self) {
let tmp=self.src_port.clone();
self.src_port=self.target_port.clone();
self.target_port=tmp;
}
} }
#[cfg(test)] #[cfg(test)]
......
...@@ -32,12 +32,13 @@ pub struct NodeGroup { ...@@ -32,12 +32,13 @@ pub struct NodeGroup {
expand_view: bool, expand_view: bool,
input_port_map: HashMap<String, (NodeIndex, String)>, input_port_map: HashMap<String, (NodeIndex, String)>,
output_port_map: HashMap<String, (NodeIndex, String)>, output_port_map: HashMap<String, (NodeIndex, String)>,
is_inverted: bool,
} }
impl NodeGroup { impl NodeGroup {
/// Creates a new [`NodeGroup`]. /// Creates a new [`NodeGroup`].
pub fn new() -> Self { pub fn new() -> Self {
Self{ Self {
expand_view: false, expand_view: false,
..Default::default() ..Default::default()
} }
...@@ -80,7 +81,7 @@ impl NodeGroup { ...@@ -80,7 +81,7 @@ impl NodeGroup {
.borrow() .borrow()
.ports() .ports()
.inputs() .inputs()
.contains(&target_port.into()) .contains(&target_port.into())
{ {
return Err(OpossumError::OpticScenery(format!( return Err(OpossumError::OpticScenery(format!(
"target node {} does not have a port {}", "target node {} does not have a port {}",
...@@ -334,7 +335,8 @@ impl NodeGroup { ...@@ -334,7 +335,8 @@ impl NodeGroup {
// Check if node is group src node // Check if node is group src node
let incoming_edges = if group_srcs.any(|gs| gs == idx) { let incoming_edges = if group_srcs.any(|gs| gs == idx) {
// get from incoming_data // get from incoming_data
let assigned_ports = self.input_port_map.iter().filter(|p| p.1 .0 == idx); let portmap = if self.is_inverted { &self.output_port_map} else { &self.input_port_map};
let assigned_ports = portmap.iter().filter(|p| p.1 .0 == idx);
let mut incoming = LightResult::default(); let mut incoming = LightResult::default();
for port in assigned_ports { for port in assigned_ports {
incoming.insert( incoming.insert(
...@@ -347,7 +349,10 @@ impl NodeGroup { ...@@ -347,7 +349,10 @@ impl NodeGroup {
self.incoming_edges(idx) self.incoming_edges(idx)
}; };
let node = self.g.node_weight(idx).unwrap(); let node = self.g.node_weight(idx).unwrap();
println!("Analyzing node {}", node.borrow().name());
println!("Incoming edges {:?}", incoming_edges);
let outgoing_edges = node.borrow_mut().analyze(incoming_edges, analyzer_type)?; let outgoing_edges = node.borrow_mut().analyze(incoming_edges, analyzer_type)?;
println!("Outgoing edges: {:?}", outgoing_edges);
let mut group_sinks = self.g.externals(Direction::Outgoing); let mut group_sinks = self.g.externals(Direction::Outgoing);
// Check if node is group sink node // Check if node is group sink node
if group_sinks.any(|gs| gs == idx) { if group_sinks.any(|gs| gs == idx) {
...@@ -367,11 +372,10 @@ impl NodeGroup { ...@@ -367,11 +372,10 @@ impl NodeGroup {
Ok(light_result) Ok(light_result)
} }
/// Sets the expansion flag of this [`NodeGroup`]. /// Sets the expansion flag of this [`NodeGroup`].
/// If true, the group expands and the internal nodes of this group are displayed in the dot format. /// If true, the group expands and the internal nodes of this group are displayed in the dot format.
/// If false, only the group node itself is displayed and the internal setup is not shown /// If false, only the group node itself is displayed and the internal setup is not shown
pub fn shall_expand(&self) -> bool{ pub fn shall_expand(&self) -> bool {
self.expand_view self.expand_view
} }
...@@ -379,47 +383,56 @@ impl NodeGroup { ...@@ -379,47 +383,56 @@ impl NodeGroup {
/// parameters: /// parameters:
/// - port_name: name of the external port of the group /// - port_name: name of the external port of the group
/// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ... /// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ...
/// ///
/// Error, if the port is not mapped as input or output /// Error, if the port is not mapped as input or output
pub fn get_mapped_port_str(&self, port_name: &str, parent_identifier: String) -> Result<String> { pub fn get_mapped_port_str(
&self,
port_name: &str,
parent_identifier: String,
) -> Result<String> {
if self.shall_expand() { if self.shall_expand() {
if self.input_port_map.contains_key(port_name){ if self.input_port_map.contains_key(port_name) {
let port = self.input_port_map let port = self.input_port_map.get(port_name).unwrap();
.get(port_name)
.unwrap(); Ok(format!(
"{}_i{}:{}",
Ok(format!("{}_i{}:{}", parent_identifier, port.0.index(), port.1)) parent_identifier,
} port.0.index(),
else if self.output_port_map.contains_key(port_name){ port.1
let port = self.output_port_map ))
.get(port_name) } else if self.output_port_map.contains_key(port_name) {
.unwrap(); let port = self.output_port_map.get(port_name).unwrap();
Ok(format!("{}_i{}:{}", parent_identifier, port.0.index(), port.1)) Ok(format!(
} "{}_i{}:{}",
else{ parent_identifier,
Err(OpossumError::OpticGroup(format!("port {} is not mapped", port_name))) port.0.index(),
port.1
))
} else {
Err(OpossumError::OpticGroup(format!(
"port {} is not mapped",
port_name
)))
} }
} } else {
else{
Ok(format!("{}:{}", parent_identifier, port_name)) Ok(format!("{}:{}", parent_identifier, port_name))
} }
} }
/// returns the boolean which defines whether the group expands or not. /// returns the boolean which defines whether the group expands or not.
pub fn expand_view(&mut self, expand_view:bool){ pub fn expand_view(&mut self, expand_view: bool) {
self.expand_view = expand_view; self.expand_view = expand_view;
} }
/// downcasts this "OpticNode" with trait "OpicComponent" to its actual struct format "NodeGroup" /// downcasts this "OpticNode" with trait "OpicComponent" to its actual struct format "NodeGroup"
/// parameters: /// parameters:
/// - ref_node: reference to the borrowed node of a graph /// - ref_node: reference to the borrowed node of a graph
/// ///
/// Returns a reference to the NodeGroup struct /// Returns a reference to the NodeGroup struct
/// ///
/// Error, if the OpticNode can not be casted to the type of NodeGroup /// Error, if the OpticNode can not be casted to the type of NodeGroup
fn cast_node_to_group<'a>(&self, ref_node: &'a OpticNode) -> Result<&'a NodeGroup>{ fn cast_node_to_group<'a>(&self, ref_node: &'a OpticNode) -> Result<&'a NodeGroup> {
let node_boxed = (&*ref_node).node(); let node_boxed = (&*ref_node).node();
let downcasted_node = node_boxed.downcast_ref::<NodeGroup>(); let downcasted_node = node_boxed.downcast_ref::<NodeGroup>();
...@@ -431,15 +444,13 @@ impl NodeGroup { ...@@ -431,15 +444,13 @@ impl NodeGroup {
} }
} }
/// checks if the contained node is a group_node itself.
/// checks if the contained node is a group_node itself.
/// Returns true, if the node is a group /// Returns true, if the node is a group
/// Returns false otherwise /// Returns false otherwise
fn check_if_group(&self, node_ref: &OpticNode) -> bool{ fn check_if_group(&self, node_ref: &OpticNode) -> bool {
if node_ref.node_type() == "group"{ if node_ref.node_type() == "group" {
true true
} } else {
else{
false false
} }
} }
...@@ -449,18 +460,26 @@ impl NodeGroup { ...@@ -449,18 +460,26 @@ impl NodeGroup {
/// - end_node_idx: NodeIndex of the node that should be connected /// - end_node_idx: NodeIndex of the node that should be connected
/// - light_port: port name that should be connected /// - light_port: port name that should be connected
/// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ... /// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ...
/// ///
/// Returns the result of the edge strnig for the dot format /// Returns the result of the edge strnig for the dot format
fn create_node_edge_str(&self, end_node_idx: NodeIndex, light_port: &str, mut parent_identifier: String) -> Result<String>{ fn create_node_edge_str(
&self,
end_node_idx: NodeIndex,
light_port: &str,
mut parent_identifier: String,
) -> Result<String> {
let node = self.g.node_weight(end_node_idx).unwrap().borrow(); let node = self.g.node_weight(end_node_idx).unwrap().borrow();
parent_identifier = if parent_identifier == "" {format!("i{}", end_node_idx.index())} else {format!("{}_i{}", &parent_identifier, end_node_idx.index())}; parent_identifier = if parent_identifier == "" {
format!("i{}", end_node_idx.index())
} else {
format!("{}_i{}", &parent_identifier, end_node_idx.index())
};
if self.check_if_group(&node){ if self.check_if_group(&node) {
let group_node = self.cast_node_to_group(&node)?; let group_node = self.cast_node_to_group(&node)?;
Ok(group_node.get_mapped_port_str(light_port, parent_identifier)?) Ok(group_node.get_mapped_port_str(light_port, parent_identifier)?)
} } else {
else{
Ok(format!("{}:{}", parent_identifier, light_port)) Ok(format!("{}:{}", parent_identifier, light_port))
} }
} }
...@@ -471,11 +490,21 @@ impl NodeGroup { ...@@ -471,11 +490,21 @@ impl NodeGroup {
/// - name: name of the node /// - name: name of the node
/// - inverted: boolean that descries wether the node is inverted or not /// - inverted: boolean that descries wether the node is inverted or not
/// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ... /// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ...
/// ///
/// Returns the result of the dot string that describes this node /// Returns the result of the dot string that describes this node
fn to_dot_expanded_view(&self,node_index: &str, name: &str, inverted: bool, mut parent_identifier: String) -> Result<String>{ fn to_dot_expanded_view(
&self,
node_index: &str,
name: &str,
inverted: bool,
mut parent_identifier: String,
) -> Result<String> {
let inv_string = if inverted { "(inv)" } else { "" }; let inv_string = if inverted { "(inv)" } else { "" };
parent_identifier = if parent_identifier == "" {format!("i{}", node_index)} else {format!("{}_i{}", &parent_identifier, node_index)}; parent_identifier = if parent_identifier == "" {
format!("i{}", node_index)
} else {
format!("{}_i{}", &parent_identifier, node_index)
};
let mut dot_string = format!( let mut dot_string = format!(
" subgraph {} {{\n\tlabel=\"{}{}\"\n\tfontsize=15\n\tcluster=true\n\t", " subgraph {} {{\n\tlabel=\"{}{}\"\n\tfontsize=15\n\tcluster=true\n\t",
parent_identifier, name, inv_string parent_identifier, name, inv_string
...@@ -483,14 +512,24 @@ impl NodeGroup { ...@@ -483,14 +512,24 @@ impl NodeGroup {
for node_idx in self.g.node_indices() { for node_idx in self.g.node_indices() {
let node = self.g.node_weight(node_idx).unwrap(); let node = self.g.node_weight(node_idx).unwrap();
dot_string += &node.borrow().to_dot(&format!("{}", node_idx.index()), parent_identifier.clone())?; dot_string += &node
.borrow()
.to_dot(&format!("{}", node_idx.index()), parent_identifier.clone())?;
} }
for edge in self.g.edge_indices() { for edge in self.g.edge_indices() {
let light: &Light = self.g.edge_weight(edge).unwrap(); let light: &Light = self.g.edge_weight(edge).unwrap();
let end_nodes = self.g.edge_endpoints(edge).unwrap(); let end_nodes = self.g.edge_endpoints(edge).unwrap();
let src_edge_str = self.create_node_edge_str(end_nodes.0, light.src_port(), parent_identifier.clone())?; let src_edge_str = self.create_node_edge_str(
let target_edge_str = self.create_node_edge_str(end_nodes.1, light.target_port(), parent_identifier.clone())?; end_nodes.0,
light.src_port(),
parent_identifier.clone(),
)?;
let target_edge_str = self.create_node_edge_str(
end_nodes.1,
light.target_port(),
parent_identifier.clone(),
)?;
dot_string.push_str(&format!(" {} -> {} \n", src_edge_str, target_edge_str)); dot_string.push_str(&format!(" {} -> {} \n", src_edge_str, target_edge_str));
// needed when multiple ports can be assigned // needed when multiple ports can be assigned
...@@ -513,17 +552,42 @@ impl NodeGroup { ...@@ -513,17 +552,42 @@ impl NodeGroup {
/// - inverted: boolean that descries wether the node is inverted or not /// - inverted: boolean that descries wether the node is inverted or not
/// - _ports: /// - _ports:
/// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ... /// - parent_identifier: String that contains the hierarchical structure: parentidx_childidx_childofchildidx ...
/// ///
/// Returns the result of the dot string that describes this node /// Returns the result of the dot string that describes this node
fn to_dot_collapsed_view(&self,node_index: &str, name: &str, inverted: bool, _ports: &OpticPorts, mut parent_identifier: String) -> Result<String>{ fn to_dot_collapsed_view(
&self,
node_index: &str,
name: &str,
inverted: bool,
_ports: &OpticPorts,
mut parent_identifier: String,
) -> Result<String> {
let inv_string = if inverted { " (inv)" } else { "" }; let inv_string = if inverted { " (inv)" } else { "" };
let node_name = format!("{}{}", name, inv_string); let node_name = format!("{}{}", name, inv_string);
parent_identifier = if parent_identifier == "" {format!("i{}", node_index)} else {format!("{}_i{}", &parent_identifier, node_index)}; parent_identifier = if parent_identifier == "" {
format!("i{}", node_index)
} else {
format!("{}_i{}", &parent_identifier, node_index)
};
let mut dot_str = format!("\t{} [\n\t\tshape=plaintext\n", parent_identifier); let mut dot_str = format!("\t{} [\n\t\tshape=plaintext\n", parent_identifier);
let mut indent_level = 2; let mut indent_level = 2;
dot_str.push_str(&self.add_html_like_labels(&node_name, &mut indent_level, _ports, inverted)); dot_str.push_str(&self.add_html_like_labels(
&node_name,
&mut indent_level,
_ports,
inverted,
));
Ok(dot_str) Ok(dot_str)
} }
fn invert_graph(&mut self) {
for node in self.g.node_weights_mut() {
node.borrow_mut().set_inverted(true);
}
for edge in self.g.edge_weights_mut() {
edge.inverse();
}
self.g.reverse();
}
} }
impl Optical for NodeGroup { impl Optical for NodeGroup {
...@@ -548,15 +612,26 @@ impl Optical for NodeGroup { ...@@ -548,15 +612,26 @@ impl Optical for NodeGroup {
) -> Result<LightResult> { ) -> Result<LightResult> {
self.analyze_group(incoming_data, analyzer_type) self.analyze_group(incoming_data, analyzer_type)
} }
fn set_inverted(&mut self, inverted: bool) {
if inverted {
self.invert_graph();
}
self.is_inverted = inverted;
}
} }
impl Dottable for NodeGroup { impl Dottable for NodeGroup {
fn to_dot(&self, node_index: &str, name: &str, inverted: bool, _ports: &OpticPorts, parent_identifier: String) -> Result<String> { fn to_dot(
&self,
node_index: &str,
name: &str,
inverted: bool,
_ports: &OpticPorts,
parent_identifier: String,
) -> Result<String> {
if self.expand_view { if self.expand_view {
self.to_dot_expanded_view(node_index, name, inverted, parent_identifier) self.to_dot_expanded_view(node_index, name, inverted, parent_identifier)
} } else {
else{
self.to_dot_collapsed_view(node_index, name, inverted, _ports, parent_identifier) self.to_dot_collapsed_view(node_index, name, inverted, _ports, parent_identifier)
} }
} }
...@@ -566,7 +641,6 @@ impl Dottable for NodeGroup { ...@@ -566,7 +641,6 @@ impl Dottable for NodeGroup {
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::NodeGroup; use super::NodeGroup;
......
...@@ -65,7 +65,8 @@ impl OpticNode { ...@@ -65,7 +65,8 @@ impl OpticNode {
/// ///
/// This means that the node is used in "reverse" direction. All output port become input parts and vice versa. /// This means that the node is used in "reverse" direction. All output port become input parts and vice versa.
pub fn set_inverted(&mut self, inverted: bool) { pub fn set_inverted(&mut self, inverted: bool) {
self.ports.set_inverted(inverted) self.ports.set_inverted(inverted);
self.node.set_inverted(inverted);
} }
/// Returns if the [`OpticNode`] is used in reversed direction. /// Returns if the [`OpticNode`] is used in reversed direction.
pub fn inverted(&self) -> bool { pub fn inverted(&self) -> bool {
...@@ -126,11 +127,12 @@ pub trait Optical { ...@@ -126,11 +127,12 @@ pub trait Optical {
Ok(LightResult::default()) Ok(LightResult::default())
} }
fn export_data(&self, _file_name: &str) { fn export_data(&self, _file_name: &str) {
println!("no export_data function implemented") println!("no export_data function implemented for nodetype <{}>", self.node_type())
} }
fn is_detector(&self) -> bool { fn is_detector(&self) -> bool {
false false
} }
fn set_inverted(&mut self, _inverted: bool) {}
} }
/// This trait deals with the translation of the OpticScenery-graph structure to the dot-file format which is needed to visualize the graphs /// This trait deals with the translation of the OpticScenery-graph structure to the dot-file format which is needed to visualize the graphs
...@@ -366,7 +368,7 @@ impl dyn OpticComponent + 'static { ...@@ -366,7 +368,7 @@ impl dyn OpticComponent + 'static {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::OpticNode; use super::OpticNode;
use crate::nodes::{Dummy, Detector}; use crate::nodes::{Detector, Dummy};
#[test] #[test]
fn new() { fn new() {
let node = OpticNode::new("Test", Dummy); let node = OpticNode::new("Test", Dummy);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment