Skip to content

Commit

Permalink
Better simulation algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
romancardenas committed Oct 23, 2023
1 parent a052bd8 commit 64670af
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 90 deletions.
10 changes: 3 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ devstone_busy = ["cpu-time"]
par_any = ["rayon"]
par_start = ["par_any"]
par_collection = ["par_any"]
par_eoc = ["par_any"]
par_xic = ["par_any"]
par_transition = ["par_any"]
par_stop = ["par_any"]
par_xxc = ["par_eoc", "par_xic"]
par_sim_no_xxc = ["par_collection", "par_transition"]
par_sim = ["par_sim_no_xxc", "par_xxc"]
par_all_no_xxc = ["par_start", "par_sim_no_xxc", "par_stop"]
par_all = ["par_all_no_xxc", "par_xxc"]
par_all_no_couplings = ["par_start", "par_collection", "par_transition", "par_stop"]
par_couplings = ["par_any"]
par_all = ["par_all_no_couplings", "par_couplings"]

[[example]]
name = "devstone"
Expand Down
95 changes: 50 additions & 45 deletions src/modeling/coupled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use crate::DynRef;
use std::collections::HashMap;
use std::sync::Arc;

pub(crate) type Coupling = (Arc<dyn Port>, Arc<dyn Port>);

/// Coupled DEVS model.
pub struct Coupled {
/// Component wrapped by the coupled model.
Expand All @@ -19,16 +21,16 @@ pub struct Coupled {
eoc_map: HashMap<String, HashMap<String, usize>>,
/// Components of the DEVS coupled model (serialized for better performance).
pub(crate) components: Vec<Box<dyn Simulator>>,
/// External input and internal couplings (serialized for better performance).
pub(crate) xics: Vec<(Arc<dyn Port>, Arc<dyn Port>)>,
/// External input couplings (serialized for better performance).
pub(crate) eics: Vec<Coupling>,
/// Internal couplings (serialized for better performance).
pub(crate) ics: Vec<Coupling>,
/// External output couplings (serialized for better performance).
pub(crate) eocs: Vec<(Arc<dyn Port>, Arc<dyn Port>)>,
#[cfg(feature = "par_xic")]
xic_map: HashMap<String, Vec<usize>>,
#[cfg(feature = "par_xic")]
pub(crate) par_xics: Vec<Vec<usize>>,
#[cfg(feature = "par_eoc")]
pub(crate) par_eocs: Vec<Vec<usize>>,
pub(crate) eocs: Vec<Coupling>,
#[cfg(feature = "par_couplings")]
pub(crate) par_eics: Vec<Vec<Coupling>>,
#[cfg(feature = "par_couplings")]
pub(crate) par_xxcs: Vec<Vec<Coupling>>,
}

impl Coupled {
Expand All @@ -41,14 +43,13 @@ impl Coupled {
ic_map: HashMap::new(),
eoc_map: HashMap::new(),
components: Vec::new(),
xics: Vec::new(),
eics: Vec::new(),
ics: Vec::new(),
eocs: Vec::new(),
#[cfg(feature = "par_xic")]
xic_map: HashMap::new(),
#[cfg(feature = "par_xic")]
par_xics: Vec::new(),
#[cfg(feature = "par_eoc")]
par_eocs: Vec::new(),
#[cfg(feature = "par_couplings")]
par_eics: Vec::new(),
#[cfg(feature = "par_couplings")]
par_xxcs: Vec::new(),
}
}

Expand Down Expand Up @@ -139,16 +140,8 @@ impl Coupled {
if coups.contains_key(&source_key) {
panic!("coupling already exists");
}
coups.insert(source_key, self.xics.len());
#[cfg(feature = "par_xic")]
{
let destination_key = component_to.to_string() + "-" + port_to;
self.xic_map
.entry(destination_key)
.or_default()
.push(self.xics.len());
}
self.xics.push((p_to, p_from));
coups.insert(source_key, self.eics.len());
self.eics.push((p_to, p_from));
}

/// Adds a new IC to the model.
Expand Down Expand Up @@ -189,16 +182,8 @@ impl Coupled {
if coups.contains_key(&source_key) {
panic!("coupling already exists");
}
coups.insert(source_key, self.xics.len());
#[cfg(feature = "par_xic")]
{
let destination_key = component_to.to_string() + "-" + port_to;
self.xic_map
.entry(destination_key)
.or_default()
.push(self.xics.len());
}
self.xics.push((p_to, p_from));
coups.insert(source_key, self.ics.len());
self.ics.push((p_to, p_from));
}

/// Adds a new EOC to the model.
Expand Down Expand Up @@ -234,19 +219,39 @@ impl Coupled {
self.eocs.push((p_to, p_from));
}

#[cfg(feature = "par_xic")]
#[cfg(feature = "par_couplings")]
#[inline]
pub(crate) fn build_par_xics(&mut self) {
self.par_xics = self.xic_map.values().cloned().collect();
pub(crate) fn build_par_eics(&mut self) {
for coups in self.eic_map.values() {
let mut x = Vec::new();
for &source in coups.values() {
x.push(self.eics[source].clone());
}
self.par_eics.push(x);
}
}

#[cfg(feature = "par_eoc")]
#[cfg(feature = "par_couplings")]
#[inline]
pub(crate) fn build_par_ics(&mut self) {
for coups in self.ic_map.values() {
let mut x = Vec::new();
for &source in coups.values() {
x.push(self.ics[source].clone());
}
self.par_xxcs.push(x);
}
}

#[cfg(feature = "par_couplings")]
#[inline]
pub(crate) fn build_par_eocs(&mut self) {
self.par_eocs = self
.eoc_map
.values()
.map(|m| m.values().copied().collect())
.collect();
for coups in self.eoc_map.values() {
let mut x = Vec::new();
for &source in coups.values() {
x.push(self.eocs[source].clone());
}
self.par_xxcs.push(x);
}
}
}
109 changes: 71 additions & 38 deletions src/simulation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,21 @@ pub trait Simulator: DynRef {
self.get_component_mut().set_sim_t(t_last, t_next);
}

#[inline]
fn clear_input(&mut self) {
// Safety: simulator clearing its input
unsafe { self.get_component_mut().clear_input() };
}

#[inline]
fn clear_output(&mut self) {
// Safety: simulator clearing its output
unsafe { self.get_component_mut().clear_output() };
}

/// Removes all the messages from all the ports.
#[inline]
fn clear_ports(&mut self) {
fn clear(&mut self) {
let component = self.get_component_mut();
// Safety: simulator clearing its ports
unsafe {
Expand Down Expand Up @@ -95,18 +107,20 @@ impl<T: Atomic + DynRef> Simulator for T {
if !unsafe { self.get_component().is_input_empty() } {
if t == t_next {
Atomic::delta_conf(self);
self.clear_output();
} else {
let e = t - self.get_t_last();
Atomic::delta_ext(self, e);
}
self.clear_input();
} else if t == t_next {
Atomic::delta_int(self);
self.clear_output();
} else {
return t_next;
}
let t_next = t + Atomic::ta(self);
self.set_sim_t(t, t_next);
self.clear_ports();
t_next
}
}
Expand Down Expand Up @@ -139,10 +153,12 @@ impl Simulator for Coupled {
// and set the inner component's last and next times
self.set_sim_t(t_start, t_next);

#[cfg(feature = "par_xic")]
self.build_par_xics();
#[cfg(feature = "par_eoc")]
self.build_par_eocs();
#[cfg(feature = "par_couplings")]
{
self.build_par_eics();
self.build_par_eocs();
self.build_par_ics();
}

t_next
}
Expand Down Expand Up @@ -174,19 +190,25 @@ impl Simulator for Coupled {
let iter = self.components.iter_mut();
iter.for_each(|c| c.collection(t));

#[cfg(feature = "par_eoc")]
self.par_eocs.par_iter().for_each(|coups| {
for &i in coups.iter() {
let (port_to, port_from) = &self.eocs[i];
#[cfg(feature = "par_couplings")]
self.par_xxcs.par_iter().for_each(|coups| {
coups.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
}
});
#[cfg(not(feature = "par_eoc"))]
self.eocs.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
});
});

#[cfg(not(feature = "par_couplings"))]
{
self.eocs.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
});
self.ics.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
});
}
}
}

Expand All @@ -199,30 +221,41 @@ impl Simulator for Coupled {
///
/// If the feature `par_transition` is activated, the iteration is parallelized.
fn transition(&mut self, t: f64) -> f64 {
#[cfg(feature = "par_xic")]
self.par_xics.par_iter().for_each(|coups| {
for &i in coups.iter() {
let (port_to, port_from) = &self.xics[i];
// Safety: simulator checking if its input is empty
let is_external = !unsafe { self.get_component().is_input_empty() };
// Propagate messages according to EICs only if there are messages in the input ports
if is_external {
#[cfg(feature = "par_couplings")]
self.par_eics.par_iter().for_each(|coups| {
coups.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
});
});
#[cfg(not(feature = "par_couplings"))]
self.eics.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
}
});
#[cfg(not(feature = "par_xic"))]
self.xics.iter().for_each(|(port_to, port_from)| {
// Safety: coupled model propagating messages
unsafe { port_from.propagate(&**port_to) };
});
#[cfg(feature = "par_transition")]
let iterator = self.components.par_iter_mut();
#[cfg(not(feature = "par_transition"))]
let iterator = self.components.iter_mut();
let t_next = iterator
.map(|c| c.transition(t))
.min_by(|a, b| a.total_cmp(b))
.unwrap_or(f64::INFINITY);
self.set_sim_t(t, t_next);
self.clear_ports();
t_next
});
self.clear_input();
}
let is_internal = t >= self.get_t_next();
if is_internal {
self.clear_output();
}
// Nested call only if there are messages in the input ports or if the time has come
if is_external || is_internal {
#[cfg(feature = "par_transition")]
let iterator = self.components.par_iter_mut();
#[cfg(not(feature = "par_transition"))]
let iterator = self.components.iter_mut();
let t_next = iterator
.map(|c| c.transition(t))
.min_by(|a, b| a.total_cmp(b))
.unwrap_or(f64::INFINITY);
self.set_sim_t(t, t_next);
}
self.get_t_next()
}
}

Expand Down

0 comments on commit 64670af

Please sign in to comment.