Skip to content
Snippets Groups Projects
Commit 806919f6 authored by y.zobus's avatar y.zobus
Browse files

Merge branch 'main' of git.gsi.de:phelix/rust/opossum

parents edcac3c3 48cd0ab1
No related branches found
No related tags found
No related merge requests found
......@@ -51,9 +51,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.59"
version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
dependencies = [
"unicode-ident",
]
......@@ -69,15 +69,15 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.163"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
[[package]]
name = "serde_derive"
version = "1.0.163"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
......
......@@ -12,7 +12,7 @@ fn main() {
println!("export to `dot` format: {}", scenery.to_dot());
let node1 = scenery.add_element("my optic", NodeDummy);
let node2 = scenery.add_element("my other optic", NodeDummy);
if let Ok(_) = scenery.connect_nodes(node1, node2) {
if let Ok(_) = scenery.connect_nodes(node1, "rear", node2, "front") {
let path = "graph.dot";
let mut output = File::create(path).unwrap();
write!(output, "{}", scenery.to_dot()).unwrap();
......
......@@ -28,12 +28,12 @@ fn main() {
node.set_inverted(true);
let n1i = scenery.add_node(node);
scenery.connect_nodes(n1, n2).unwrap();
scenery.connect_nodes(n2, n3).unwrap();
scenery.connect_nodes(n3, n4).unwrap();
scenery.connect_nodes(n4, n3i).unwrap();
scenery.connect_nodes(n3i, n2i).unwrap();
scenery.connect_nodes(n2i, n1i).unwrap();
scenery.connect_nodes(n1, "rear", n2, "front").unwrap();
scenery.connect_nodes(n2, "rear", n3, "front").unwrap();
scenery.connect_nodes(n3, "rear", n4, "front").unwrap();
scenery.connect_nodes(n4, "rear", n3i, "rear").unwrap();
scenery.connect_nodes(n3i, "front", n2i, "rear").unwrap();
scenery.connect_nodes(n2i, "front", n1i, "rear").unwrap();
let mut group = NodeGroup::new();
let g_n1 = group.add_node(OpticNode::new("Beamsplitter", NodeDummy));
......
use opossum::nodes::{NodeBeamSplitter, NodeDummy};
use opossum::optic_scenery::OpticScenery;
use opossum::nodes::{NodeDummy, NodeBeamSplitter};
use std::fs::File;
use std::io::Write;
......@@ -21,15 +21,30 @@ fn main() {
let pump_shg_node = scenery.add_element("Pump SHG", NodeDummy);
let pump_splitter_node = scenery.add_element("Pump Beam Splitter", NodeBeamSplitter);
scenery.connect_nodes(pulse_generation_split_node, uOPA_1_node);
scenery.connect_nodes(pulse_generation_split_node, pump_pre_amplifier_node);
scenery.connect_nodes(pump_pre_amplifier_node, pump_main_amplifier_node);
scenery.connect_nodes(pump_main_amplifier_node, pump_compressor_node);
scenery.connect_nodes(pump_compressor_node, pump_shg_node);
scenery.connect_nodes(pump_shg_node, pump_splitter_node);
scenery.connect_nodes(pump_splitter_node, uOPA_1_node);
scenery.connect_nodes(uOPA_1_node, uOPA_2_node);
scenery.connect_nodes(pump_splitter_node, uOPA_2_node);
scenery.connect_nodes(pulse_generation_split_node, "rear", uOPA_1_node, "front");
scenery.connect_nodes(
pulse_generation_split_node,
"rear",
pump_pre_amplifier_node,
"front",
);
scenery.connect_nodes(
pump_pre_amplifier_node,
"rear",
pump_main_amplifier_node,
"front",
);
scenery.connect_nodes(
pump_main_amplifier_node,
"rear",
pump_compressor_node,
"front",
);
scenery.connect_nodes(pump_compressor_node, "rear", pump_shg_node, "front");
scenery.connect_nodes(pump_shg_node, "rear", pump_splitter_node, "front");
scenery.connect_nodes(pump_splitter_node, "transmitted", uOPA_1_node, "front").unwrap();
scenery.connect_nodes(uOPA_1_node, "rear", uOPA_2_node, "front");
scenery.connect_nodes(pump_splitter_node, "reflected", uOPA_2_node, "front").unwrap();
let mut scenery_2 = OpticScenery::new();
scenery_2.set_description("PHELIX uOPA Pump Pre-Amplifier".into());
......@@ -54,25 +69,25 @@ fn main() {
let monitor2_node = scenery_2.add_element("Monitor", NodeDummy);
let monitor3_node = scenery_2.add_element("Monitor", NodeDummy);
scenery_2.connect_nodes(spm_node, circ1_node);
scenery_2.connect_nodes(circ1_node, circ2_node);
scenery_2.connect_nodes(circ2_node, cfbg_node);
scenery_2.connect_nodes(cfbg_node, circ3_node);
scenery_2.connect_nodes(cfbg_node, monitor1_node);
scenery_2.connect_nodes(circ3_node, isolator1_node);
scenery_2.connect_nodes(isolator1_node, tap1_node);
scenery_2.connect_nodes(tap1_node, monitor2_node);
scenery_2.connect_nodes(tap1_node, wdm_node);
scenery_2.connect_nodes(diode1_node, wdm_node);
scenery_2.connect_nodes(wdm_node, yb_fiber1_node);
scenery_2.connect_nodes(yb_fiber1_node, tap2_node);
scenery_2.connect_nodes(tap2_node, monitor3_node);
scenery_2.connect_nodes(tap2_node, aom_node);
scenery_2.connect_nodes(aom_node, isolator2_node);
scenery_2.connect_nodes(isolator2_node, yb_fiber2_node_node);
scenery_2.connect_nodes(yb_fiber2_node_node, dichroic_node);
scenery_2.connect_nodes(dichroic_node, dichroic_node);
scenery_2.connect_nodes(diode2_node, dichroic_node);
scenery_2.connect_nodes(spm_node, "rear", circ1_node, "front");
scenery_2.connect_nodes(circ1_node, "rear", circ2_node, "front");
scenery_2.connect_nodes(circ2_node, "rear", cfbg_node, "front");
scenery_2.connect_nodes(cfbg_node, "rear", circ3_node, "front");
scenery_2.connect_nodes(cfbg_node, "rear", monitor1_node, "front");
scenery_2.connect_nodes(circ3_node, "rear", isolator1_node, "front");
scenery_2.connect_nodes(isolator1_node, "rear", tap1_node, "front");
scenery_2.connect_nodes(tap1_node, "rear", monitor2_node, "front");
scenery_2.connect_nodes(tap1_node, "rear", wdm_node, "front");
scenery_2.connect_nodes(diode1_node, "rear", wdm_node, "front");
scenery_2.connect_nodes(wdm_node, "rear", yb_fiber1_node, "front");
scenery_2.connect_nodes(yb_fiber1_node, "rear", tap2_node, "front");
scenery_2.connect_nodes(tap2_node, "rear", monitor3_node, "front");
scenery_2.connect_nodes(tap2_node, "rear", aom_node, "front");
scenery_2.connect_nodes(aom_node, "rear", isolator2_node, "front");
scenery_2.connect_nodes(isolator2_node, "rear", yb_fiber2_node_node, "front");
scenery_2.connect_nodes(yb_fiber2_node_node, "rear", dichroic_node, "front");
scenery_2.connect_nodes(dichroic_node, "rear", dichroic_node, "front");
scenery_2.connect_nodes(diode2_node, "rear", dichroic_node, "front");
let mut scenery_3 = OpticScenery::new();
scenery_3.set_description("PHELIX uOPA Pump Regenerative Main-Amplifier".into());
......
use std::{error::Error, fmt::Display};
#[derive(Debug, Clone)]
pub enum OpossumError {
OpticScenery(String),
OpticGroup(String),
OpticPort(String),
Other(String)
OpticScenery(String),
OpticGroup(String),
OpticPort(String),
Other(String),
}
impl Display for OpossumError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
OpossumError::OpticScenery(m) => {
write!(f, "Opossum Error::OpticScenery::{}", m)
}
OpossumError::OpticGroup(m) => {
write!(f, "Opossum Error::OpticGroup::{}", m)
}
OpossumError::OpticPort(m) => {
write!(f, "Opossum Error::OpticPort::{}", m)
}
OpossumError::Other(m) => write!(f, "Opossum Error::Other::{}", m),
}
}
}
impl Error for OpossumError {}
impl std::convert::From<String> for OpossumError {
fn from(msg: String) -> Self {
Self::Other(msg)
}
}
\ No newline at end of file
......@@ -4,6 +4,8 @@
pub mod optic_scenery;
/// The basic structure representing an optical element
pub mod optic_node;
pub mod light;
pub mod optic_ports;
pub mod nodes;
......
#[derive(Debug)]
pub struct Light {
src_port: String,
target_port: String,
}
impl Light {
pub fn new(src_port: &str, target_port: &str) -> Self {
Self {
src_port: src_port.into(),
target_port: target_port.into(),
}
}
pub fn src_port(&self) -> &str {
self.src_port.as_ref()
}
pub fn target_port(&self) -> &str {
self.target_port.as_ref()
}
pub fn set_src_port(&mut self, src_port: String) {
self.src_port = src_port;
}
pub fn set_target_port(&mut self, target_port: String) {
self.target_port = target_port;
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn new() {
let light = Light::new("test1", "test2");
assert_eq!(light.src_port, "test1");
assert_eq!(light.target_port, "test2");
}
#[test]
fn src_port() {
let light = Light::new("test1", "test2");
assert_eq!(light.src_port(), "test1");
}
#[test]
fn target_port() {
let light = Light::new("test1", "test2");
assert_eq!(light.target_port(), "test2");
}
#[test]
fn set_src_port() {
let mut light = Light::new("test1", "test2");
light.set_src_port("test3".into());
assert_eq!(light.src_port, "test3");
}
#[test]
fn set_target_port() {
let mut light = Light::new("test1", "test2");
light.set_target_port("test3".into());
assert_eq!(light.target_port, "test3");
}
}
use std::rc::{Weak, Rc};
use crate::optic_node::{OpticNode, Optical};
/// A virtual component referring to another existing component. This node type is necessary in order to model resonators (loops) or double-pass systems.
pub struct NodeReference<'a> {
reference: &'a OpticNode,
pub struct NodeReference {
reference: Rc<OpticNode>,
}
impl<'a> NodeReference<'a> {
pub fn new(node: &'a OpticNode) -> Self {
impl NodeReference {
pub fn new(node: Rc<OpticNode>) -> Self {
Self { reference: node }
}
}
impl<'a> Optical for NodeReference<'a> {
impl Optical for NodeReference {
fn node_type(&self) -> &str {
"reference"
}
......
use std::fmt::Debug;
use std::{fmt::Debug, rc::Rc};
use crate::optic_ports::OpticPorts;
/// An [`OpticNode`] is the basic struct representing an optical component.
......@@ -18,7 +18,7 @@ impl OpticNode {
/// use opossum::optic_node::OpticNode;
/// use opossum::nodes::NodeDummy;
///
/// let node=OpticNode::new("My node", Box::new(NodeDummy));
/// let node=OpticNode::new("My node", NodeDummy);
/// ```
pub fn new<T: Optical+ 'static>(name: &str, node_type: T) -> Self {
let ports=node_type.ports();
......
use std::rc::Rc;
use crate::error::OpossumError;
use crate::light::Light;
use crate::optic_node::{OpticNode, Optical};
use petgraph::algo::*;
use petgraph::prelude::{DiGraph, EdgeIndex, NodeIndex};
......@@ -9,7 +12,7 @@ type Result<T> = std::result::Result<T, OpossumError>;
/// to be added to this structure in order to be considered for an analysis.
#[derive(Default, Debug)]
pub struct OpticScenery {
g: DiGraph<OpticNode, ()>,
g: DiGraph<Rc<OpticNode>, Light>,
description: String,
}
......@@ -23,7 +26,7 @@ impl OpticScenery {
/// This command just adds an [`OpticNode`] to the graph. It does not connect
/// it to existing nodes in the graph. The given optical element is consumed (owned) by the [`OpticScenery`].
pub fn add_node(&mut self, node: OpticNode) -> NodeIndex {
self.g.add_node(node)
self.g.add_node(Rc::new(node))
}
/// Add a given optical element to the graph of this [`OpticScenery`].
///
......@@ -31,7 +34,7 @@ impl OpticScenery {
/// it to existing nodes in the graph. The given optical element is consumed (owned) by the [`OpticScenery`]. Internally the corresponding [`OpticNode`] is
/// automatically generated. It serves as a short-cut to the `add_node` function.
pub fn add_element<T: Optical + 'static>(&mut self, name: &str, t: T) -> NodeIndex {
self.g.add_node(OpticNode::new(name, t))
self.g.add_node(Rc::new(OpticNode::new(name, t)))
}
/// Get reference of [`OpticNode`].
///
......@@ -50,19 +53,51 @@ impl OpticScenery {
pub fn connect_nodes(
&mut self,
src_node: NodeIndex,
src_port: &str,
target_node: NodeIndex,
target_port: &str,
) -> Result<EdgeIndex> {
if self.g.node_weight(src_node).is_none() {
if let Some(source) = self.g.node_weight(src_node) {
if !source.ports().outputs().contains(&src_port.into()) {
return Err(OpossumError::OpticScenery(format!(
"source node {} does not have a port {}",
source.name(),
src_port
)));
}
} else {
return Err(OpossumError::OpticScenery(
"source node with gievn index does not exist".into(),
"source node with given index does not exist".into(),
));
}
if self.g.node_weight(target_node).is_none() {
if let Some(target) = self.g.node_weight(target_node) {
if !target.ports().inputs().contains(&target_port.into()) {
return Err(OpossumError::OpticScenery(format!(
"target node {} does not have a port {}",
target.name(),
target_port
)));
}
} else {
return Err(OpossumError::OpticScenery(
"target node with given index does not exist".into(),
));
}
let edge_index = self.g.add_edge(src_node, target_node, ());
if self.src_node_port_exists(src_node, src_port) {
return Err(OpossumError::OpticScenery(format!(
"src node with given port {} is already connected",
src_port
)));
}
if self.target_node_port_exists(src_node, src_port) {
return Err(OpossumError::OpticScenery(format!(
"target node with given port {} is already connected",
target_port
)));
}
let edge_index = self
.g
.add_edge(src_node, target_node, Light::new(src_port, target_port));
if is_cyclic_directed(&self.g) {
self.g.remove_edge(edge_index);
return Err(OpossumError::OpticScenery(
......@@ -71,6 +106,30 @@ impl OpticScenery {
}
Ok(edge_index)
}
fn src_node_port_exists(&self, src_node: NodeIndex, src_port: &str) -> bool {
self.g
.edges_directed(src_node, petgraph::Direction::Outgoing)
.any(|e| e.weight().src_port() == src_port)
}
fn target_node_port_exists(&self, target_node: NodeIndex, target_port: &str) -> bool {
self.g
.edges_directed(target_node, petgraph::Direction::Incoming)
.any(|e| e.weight().target_port() == target_port)
}
/// Return a reference to the [`OpticNode`] specifiec by the node index.
///
/// This function is mainly useful for setting up a reference node.
///
/// # Errors
///
/// This function will return an error if the node does not exist.
pub fn node_ref(&self, node: NodeIndex) ->Result<Rc<OpticNode>> {
if let Some(node) = self.g.node_weight(node) {
Ok(node.to_owned())
} else {
Err(OpossumError::OpticScenery("node index does not exist".into()))
}
}
/// Export the optic graph into the `dot` format to be used in combination with the [`graphviz`](https://graphviz.org/) software.
pub fn to_dot(&self) -> String {
let mut dot_string = "digraph {\n".to_owned();
......@@ -83,11 +142,14 @@ impl OpticScenery {
dot_string += &node.to_dot(&format!("i{}", node_idx.index()));
}
for edge in self.g.edge_indices() {
let light=self.g.edge_weight(edge).unwrap();
let end_nodes = self.g.edge_endpoints(edge).unwrap();
dot_string.push_str(&format!(
" i{} -> i{}\n",
" i{} -> i{} [label=\"{}->{}\"]\n",
end_nodes.0.index(),
end_nodes.1.index()
end_nodes.1.index(),
light.src_port(),
light.target_port()
));
}
dot_string += "}";
......@@ -135,7 +197,7 @@ mod test {
let mut scenery = OpticScenery::new();
let n1 = scenery.add_element("Test", NodeDummy);
let n2 = scenery.add_element("Test", NodeDummy);
assert!(scenery.connect_nodes(n1, n2).is_ok());
assert!(scenery.connect_nodes(n1, "rear", n2, "front").is_ok());
assert_eq!(scenery.g.edge_count(), 1);
}
#[test]
......@@ -143,16 +205,20 @@ mod test {
let mut scenery = OpticScenery::new();
let n1 = scenery.add_element("Test", NodeDummy);
let n2 = scenery.add_element("Test", NodeDummy);
assert!(scenery.connect_nodes(n1, NodeIndex::new(5)).is_err());
assert!(scenery.connect_nodes(NodeIndex::new(5), n2).is_err());
assert!(scenery
.connect_nodes(n1, "rear", NodeIndex::new(5), "front")
.is_err());
assert!(scenery
.connect_nodes(NodeIndex::new(5), "rear", n2, "front")
.is_err());
}
#[test]
fn connect_nodes_loop_error() {
let mut scenery = OpticScenery::new();
let n1 = scenery.add_element("Test", NodeDummy);
let n2 = scenery.add_element("Test", NodeDummy);
assert!(scenery.connect_nodes(n1, n2).is_ok());
assert!(scenery.connect_nodes(n2, n1).is_err());
assert!(scenery.connect_nodes(n1, "rear", n2, "front").is_ok());
assert!(scenery.connect_nodes(n2, "rear", n1, "front").is_err());
assert_eq!(scenery.g.edge_count(), 1);
}
#[test]
......@@ -175,12 +241,12 @@ mod test {
fn to_dot_with_edge() {
let mut scenery = OpticScenery::new();
scenery.set_description("SceneryTest".into());
let n1 = scenery.add_element("Test1", NodeDummy);
let n2 = scenery.add_element("Test2", NodeDummy);
if let Ok(_) = scenery.connect_nodes(n1, n2) {
let n1 = scenery.add_element("Test1", NodeDummy);
let n2 = scenery.add_element("Test2", NodeDummy);
if let Ok(_) = scenery.connect_nodes(n1, "rear", n2, "front") {
assert_eq!(
scenery.to_dot(),
"digraph {\n label=\"SceneryTest\"\n fontname=\"Helvetica,Arial,sans-serif\"\n node [fontname=\"Helvetica,Arial,sans-serif\"]\n edge [fontname=\"Helvetica,Arial,sans-serif\"]\n i0 [label=\"Test1\"]\n i1 [label=\"Test2\"]\n i0 -> i1\n}"
"digraph {\n label=\"SceneryTest\"\n fontname=\"Helvetica,Arial,sans-serif\"\n node [fontname=\"Helvetica,Arial,sans-serif\"]\n edge [fontname=\"Helvetica,Arial,sans-serif\"]\n i0 [label=\"Test1\"]\n i1 [label=\"Test2\"]\n i0 -> i1 [label=\"rear->front\"]\n}"
);
} else {
assert!(false);
......
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