biodivine_lib_param_bn/sbml/import/
_read_transitions.rs

1use crate::sbml::import::_read_mathml::{MathMl, read_mathml};
2use crate::sbml::import::{MATHML, SBML_QUAL, child_tags, read_unique_child};
3use roxmltree::{ExpandedName, Node};
4
5/// Maps almost directly to the SBML transition input tag.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub struct SbmlTransitionInput {
8    pub id: Option<String>, // Note that a missing ID is not entirely according to spec, but they do appear in models people use.
9    pub qual_species: String,
10    pub transition_effect: Option<String>,
11    pub sign: Option<String>,
12    pub essential: Option<bool>,
13}
14
15/// Maps almost directly to the SBML transition output tag.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct SbmlTransitionOutput {
18    pub id: Option<String>, // Note that a missing ID is not entirely according to spec, but they do appear in models people use.
19    pub qual_species: String,
20    pub transition_effect: Option<String>,
21}
22
23/// Represents an SBML transition term (note that default term should not have math in it).
24#[derive(Clone, Debug, PartialEq, Eq)]
25pub struct SbmlTransitionTerm {
26    pub result_level: u32,
27    pub math: Option<MathMl>,
28}
29
30#[derive(Clone, Debug, PartialEq, Eq)]
31pub struct SbmlTransition {
32    pub id: Option<String>,
33    pub inputs: Vec<SbmlTransitionInput>,
34    pub outputs: Vec<SbmlTransitionOutput>,
35    pub default_term: Option<SbmlTransitionTerm>, // Is none if the whole function is unspecified
36    pub function_terms: Vec<SbmlTransitionTerm>,
37}
38
39pub fn read_transitions(model: Node) -> Result<Vec<SbmlTransition>, String> {
40    let mut result = Vec::new();
41
42    let list = read_unique_child(model, (SBML_QUAL, "listOfTransitions"))?;
43
44    let transitions = list
45        .children()
46        .filter(|node| node.tag_name() == ExpandedName::from((SBML_QUAL, "transition")));
47
48    for transition in transitions {
49        result.push(read_transition(transition)?)
50    }
51
52    Ok(result)
53}
54
55pub fn read_transition(transition: Node) -> Result<SbmlTransition, String> {
56    let id = transition
57        .attribute((SBML_QUAL, "id"))
58        .map(|it| it.to_string());
59
60    // Inputs are optional when there aren't any.
61    let inputs = read_unique_child(transition, (SBML_QUAL, "listOfInputs")).ok();
62    let outputs = read_unique_child(transition, (SBML_QUAL, "listOfOutputs"))?;
63    // Terms are an optional element.
64    let terms = read_unique_child(transition, (SBML_QUAL, "listOfFunctionTerms")).ok();
65
66    let inputs = if let Some(inputs) = inputs {
67        child_tags(inputs, (SBML_QUAL, "input"))
68    } else {
69        Vec::new()
70    };
71    let outputs = child_tags(outputs, (SBML_QUAL, "output"));
72
73    let default_term = if let Some(terms) = terms {
74        let default_term = read_unique_child(terms, (SBML_QUAL, "defaultTerm"))?;
75        Some(read_transition_term(default_term, &id)?)
76    } else {
77        None
78    };
79
80    let terms = if let Some(terms) = terms {
81        child_tags(terms, (SBML_QUAL, "functionTerm"))
82    } else {
83        Vec::new()
84    };
85
86    let mut transition = SbmlTransition {
87        id: id.clone(),
88        inputs: Vec::new(),
89        outputs: Vec::new(),
90        default_term,
91        function_terms: Vec::new(),
92    };
93
94    if transition
95        .default_term
96        .as_ref()
97        .map(|t| t.math.is_some())
98        .unwrap_or(false)
99    {
100        return Err(format!("Default term in transition {:?} has math.", id));
101    }
102
103    for input in inputs {
104        transition.inputs.push(read_transition_input(input, &id)?);
105    }
106
107    for output in outputs {
108        transition
109            .outputs
110            .push(read_transition_output(output, &id)?);
111    }
112
113    for term in terms {
114        transition
115            .function_terms
116            .push(read_transition_term(term, &id)?);
117    }
118
119    Ok(transition)
120}
121
122fn read_transition_input(
123    input: Node,
124    transition_id: &Option<String>,
125) -> Result<SbmlTransitionInput, String> {
126    let species = input.attribute((SBML_QUAL, "qualitativeSpecies"));
127    let effect = input.attribute((SBML_QUAL, "transitionEffect"));
128    let sign = input.attribute((SBML_QUAL, "sign"));
129    let id = input.attribute((SBML_QUAL, "id"));
130    // WARNING: This attribute is not a part of the SBML-qual specification. We thus do not use
131    // the SBML-qual namespace for it.
132    let essential = input.attribute("essential");
133    if species.is_none() {
134        return Err(format!(
135            "Transition {:?} is missing an input species.",
136            transition_id
137        ));
138    }
139
140    Ok(SbmlTransitionInput {
141        id: id.map(|s| s.to_string()),
142        qual_species: species.unwrap().to_string(),
143        transition_effect: effect.map(|s| s.to_string()),
144        sign: sign.map(|s| s.to_string()),
145        essential: essential.map(|s| s == "true"),
146    })
147}
148
149fn read_transition_output(
150    output: Node,
151    transition_id: &Option<String>,
152) -> Result<SbmlTransitionOutput, String> {
153    let species = output.attribute((SBML_QUAL, "qualitativeSpecies"));
154    let effect = output.attribute((SBML_QUAL, "transitionEffect"));
155    let id = output.attribute((SBML_QUAL, "id"));
156    if species.is_none() {
157        return Err(format!(
158            "Transition output in {:?} is missing an output species.",
159            transition_id
160        ));
161    }
162
163    Ok(SbmlTransitionOutput {
164        id: id.map(|s| s.to_string()),
165        qual_species: species.unwrap().to_string(),
166        transition_effect: effect.map(|s| s.to_string()),
167    })
168}
169
170fn read_transition_term(
171    term: Node,
172    transition_id: &Option<String>,
173) -> Result<SbmlTransitionTerm, String> {
174    let result_level = term.attribute((SBML_QUAL, "resultLevel"));
175    if result_level.is_none() {
176        return Err(format!(
177            "Term result level not specified in transition {:?}.",
178            transition_id
179        ));
180    }
181    let result_level = result_level.unwrap();
182    let level = result_level.parse::<u32>();
183    if level.is_err() {
184        return Err(format!(
185            "Term result level is not a number in transition {:?}. {} given.",
186            transition_id, result_level
187        ));
188    }
189
190    let math = read_unique_child(term, (MATHML, "math")).ok();
191    let math = math.map(read_mathml).transpose()?;
192
193    Ok(SbmlTransitionTerm {
194        result_level: level.unwrap(),
195        math,
196    })
197}