rspice 0.1.0

Pure-Rust circuit simulation backend
Documentation
use crate::BasicComponent;
use crate::ComponentRef;
use crate::ConnectionRef;
use crate::EquationRef;
use crate::NetRef;
use crate::RspiceError;
use crate::TransientMatrix;
use crate::TransientResult;
use crate::TransientVector;

/// Electronic circuit, containing a number of connected basic components.
///
/// Each component is connected to a number of nets, which are
/// directly connected regions of wire.
pub struct Circuit {
    n_nets: usize,
    n_components: usize,
    n_connections: usize,
    // basic component, list of connected nets, index of first connection
    components: Vec<(Box<dyn BasicComponent>, Vec<usize>, usize)>,
    // connection indices for each net
    nets: Vec<Vec<usize>>,
}

impl Circuit {
    /// Create a new empty circuit.
    pub fn new() -> Self {
        Self {
            n_nets: 0,
            n_components: 0,
            n_connections: 0,
            components: Vec::new(),
            nets: Vec::new(),
        }
    }

    /// Create a new unconnected net and return a reference to it.
    pub fn createNet(&mut self) -> NetRef {
        let index = self.n_nets;
        self.n_nets += 1;
        self.nets.push(Vec::new());
        NetRef { index: index }
    }

    /// Create a new basic component and connect it to a list of nets.
    ///
    /// This method is only provided to implement new basic components,
    /// which should call it in their `create()` method.
    /// Any circuit or compound component should use the corresponding `create()` methods
    /// instead, to make sure that the correct number of nets is passed.
    ///
    /// See [BasicComponent] for more information.
    pub fn createBasicComponent(
        &mut self,
        mut component: Box<dyn BasicComponent>,
        nets: Vec<NetRef>,
    ) -> ComponentRef {
        self.n_components += 1;
        let mut raw_nets = Vec::new();
        let mut connections = Vec::new();
        let mut raw_connections = Vec::new();
        let mut equations = Vec::new();
        let first_connection = self.n_connections;
        for net in &nets {
            raw_nets.push(net.index);
            connections.push(ConnectionRef {
                index: self.n_connections,
            });
            raw_connections.push(self.n_connections);
            equations.push(EquationRef {
                index: self.n_connections * 2,
            });
            equations.push(EquationRef {
                index: self.n_connections * 2 + 1,
            });
            self.nets[net.index].push(self.n_connections);
            self.n_connections += 1;
        }
        component.initCircuit(nets, connections, equations);
        self.components
            .push((component, raw_nets, first_connection));
        ComponentRef {
            connections: raw_connections,
        }
    }

    /// Perform a transient simulation.
    ///
    /// Will run steps of `step` seconds, up to `duration` seconds.
    pub fn performTransientAnalysis(
        &mut self,
        duration: f64,
        step: f64,
    ) -> Result<TransientResult, RspiceError> {
        // create empty matrix
        let mut matrix = TransientMatrix::new(self.n_nets, self.n_connections);

        // each net gets two equations of its own
        for net in 0..self.n_nets {
            // all currents going into the net must add up to zero
            for connection in &self.nets[net] {
                *matrix.raw_current_term(net * 2, *connection) = 1.0;
            }

            // all currents' derivatives must also add up to zero
            for connection in &self.nets[net] {
                *matrix.raw_current_derivative_term(net * 2 + 1, *connection) = 1.0;
            }
        }

        // each component initializes its own equations
        for component in &mut self.components {
            component.0.initTransient(&mut matrix);
        }

        let mut result = Vec::new();

        // transient loop
        let mut t = 0.0;
        loop {
            // TODO: non-linear components
            // solve linear equation
            let result_at_t = matrix.solve()?;
            result.push(result_at_t.clone());
            if t >= duration {
                break;
            }
            // let all components update their equations
            let wrapped_result = TransientVector::new(result_at_t.into(), self.n_nets);
            for component in &mut self.components {
                component
                    .0
                    .updateTransient(&mut matrix, &wrapped_result, t, step);
            }
            t += step;
        }

        // generate result
        let mut result_components = Vec::new();
        for component in &self.components {
            result_components.push((component.1.clone(), component.2));
        }
        Ok(TransientResult {
            values_over_time: result,
            step: step,
            n_nets: self.n_nets,
            components: result_components,
        })
    }
}