sbml-sim 0.1.2

Simulator for SBML models.
use sbml_rs;

#[derive(Clone, Debug)]
pub struct UnboundSpecies {
    pub id: String,
    pub compartment: String,
    pub amount: Option<f64>,
    pub concentration: Option<f64>,
    pub sbo_term: Option<String>,
    pub substance_units: Option<String>,
    pub has_only_substance_units: bool,
    pub boundary_condition: bool,
    pub constant: bool,
    pub conversion_factor: Option<String>,
}

impl UnboundSpecies {
    pub fn from(species: &sbml_rs::Species) -> Self {
        let id = species
            .id
            .as_ref()
            .expect("ID is mandatory for Species.")
            .to_owned();
        let compartment = species
            .compartment
            .as_ref()
            .expect("Compartment attribute is mandatory for species.")
            .to_owned();
        let has_only_substance_units = species
            .has_only_substance_units
            .expect("has_only_substance_units attribute is mandatory for species.");
        let boundary_condition = species
            .boundary_condition
            .expect("Boundary condition attribute is mandatory for species.");
        let constant = species
            .constant
            .expect("Constant attribute is mandatory for species.");
        let mut amount = None;
        if let Some(value) = &species.initial_amount {
            amount = Some(value.to_owned());
        }
        let mut concentration = None;
        if let Some(value) = &species.initial_concentration {
            concentration = Some(value.to_owned());
        }
        let mut sbo_term = None;
        if let Some(value) = &species.sbo_term {
            sbo_term = Some(value.to_owned());
        }
        let mut conversion_factor = None;
        if let Some(value) = &species.conversion_factor {
            conversion_factor = Some(value.to_owned());
        }
        let mut substance_units = None;
        if let Some(value) = &species.substance_units {
            substance_units = Some(value.to_owned());
        }
        UnboundSpecies {
            id,
            compartment,
            amount,
            concentration,
            has_only_substance_units,
            boundary_condition,
            constant,
            sbo_term,
            substance_units,
            conversion_factor,
        }
    }

    pub fn to_bound(&self, amount: f64, concentration: f64) -> Species {
        Species {
            id: self.id.clone(),
            compartment: self.compartment.clone(),
            concentration,
            amount,
            sbo_term: self.sbo_term.clone(),
            substance_units: self.substance_units.clone(),
            has_only_substance_units: self.has_only_substance_units,
            boundary_condition: self.boundary_condition,
            constant: self.constant,
            conversion_factor: self.conversion_factor.clone(),
        }
    }

    pub fn to_bound_with_size(&self, size: f64) -> Result<Species, ()> {
        if self.concentration.is_some() && self.amount.is_none() {
            let concentration = self.concentration.unwrap();
            let amount = concentration * size;
            Ok(self.to_bound(amount, concentration))
        } else if self.amount.is_some() && self.concentration.is_none() {
            let amount = self.amount.unwrap();
            let concentration = amount / size;
            Ok(self.to_bound(amount, concentration))
        } else {
            Err(())
        }
    }
}

#[derive(Clone, Debug)]
pub struct Species {
    pub id: String,
    pub compartment: String,
    concentration: f64,
    amount: f64,
    pub sbo_term: Option<String>,
    pub substance_units: Option<String>,
    pub has_only_substance_units: bool,
    pub boundary_condition: bool,
    pub constant: bool,
    pub conversion_factor: Option<String>,
}

impl Species {
    pub fn from_concentration(
        species: &sbml_rs::Species,
        concentration: f64,
        compartment_size: f64,
    ) -> Self {
        let unbound_species = UnboundSpecies::from(species);
        let amount = concentration * compartment_size;
        unbound_species.to_bound(amount, concentration)
    }

    pub fn from_amount(species: &sbml_rs::Species, amount: f64, compartment_size: f64) -> Self {
        let unbound_species = UnboundSpecies::from(species);
        let concentration = amount / compartment_size;
        unbound_species.to_bound(amount, concentration)
    }
    pub fn concentration(&self) -> f64 {
        self.concentration
    }

    pub fn amount(&self) -> f64 {
        self.amount
    }

    pub fn update_compartment_size(&mut self, size: f64) {
        self.concentration = self.amount / size;
    }

    pub fn update_concentration(&mut self, concentration: f64, size: f64) {
        self.concentration = concentration;
        self.amount = concentration * size;
    }

    pub fn update_amount(&mut self, amount: f64, size: f64) {
        self.amount = amount;
        self.concentration = amount / size;
    }

    pub fn update_amount_by(&mut self, delta: f64, size: f64) {
        self.amount += delta;
        self.concentration = self.amount / size;
    }
}