1 /**
2 * @file FbcToCobraConverter.cpp
3 * @brief Implementation of a fbc 2 cobra converter.
4 * @author Frank T. Bergmann
5 *
6 * <!--------------------------------------------------------------------------
7 * This file is part of libSBML. Please visit http://sbml.org for more
8 * information about SBML, and the latest version of libSBML.
9 *
10 * Copyright (C) 2020 jointly by the following organizations:
11 * 1. California Institute of Technology, Pasadena, CA, USA
12 * 2. University of Heidelberg, Heidelberg, Germany
13 * 3. University College London, London, UK
14 *
15 * Copyright (C) 2019 jointly by the following organizations:
16 * 1. California Institute of Technology, Pasadena, CA, USA
17 * 2. University of Heidelberg, Heidelberg, Germany
18 *
19 * Copyright (C) 2013-2018 jointly by the following organizations:
20 * 1. California Institute of Technology, Pasadena, CA, USA
21 * 2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
22 * 3. University of Heidelberg, Heidelberg, Germany
23 *
24 * Copyright (C) 2009-2013 jointly by the following organizations:
25 * 1. California Institute of Technology, Pasadena, CA, USA
26 * 2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
27 *
28 * This library is free software; you can redistribute it and/or modify it
29 * under the terms of the GNU Lesser General Public License as published by
30 * the Free Software Foundation. A copy of the license agreement is provided
31 * in the file named "LICENSE.txt" included with this software distribution
32 * and also available online as http://sbml.org/software/libsbml/license.html
33 * ---------------------------------------------------------------------- -->
34 */
35
36
37 #include <sbml/packages/fbc/util/FbcToCobraConverter.h>
38 #include <sbml/conversion/SBMLConverterRegistry.h>
39 #include <sbml/conversion/SBMLConverterRegister.h>
40 #include <sbml/conversion/ConversionProperties.h>
41
42 #include <sbml/common/sbmlfwd.h>
43 #include <sbml/extension/SBasePlugin.h>
44 #include <sbml/extension/SBMLDocumentPlugin.h>
45 #include <sbml/SBMLWriter.h>
46 #include <sbml/SBMLReader.h>
47 #include <sbml/packages/fbc/common/FbcExtensionTypes.h>
48 #include <sbml/math/FormulaParser.h>
49
50 #ifdef __cplusplus
51
52 #include <algorithm>
53 #include <string>
54
55 using namespace std;
56 LIBSBML_CPP_NAMESPACE_BEGIN
57
58 /** @cond doxygenLibsbmlInternal */
59 /*
60 * SBML Converter stuff below
61 */
init()62 void FbcToCobraConverter::init()
63 {
64 SBMLConverterRegistry::getInstance().addConverter(new FbcToCobraConverter());
65 }
66 /** @endcond */
67
68
FbcToCobraConverter()69 FbcToCobraConverter::FbcToCobraConverter()
70 : SBMLConverter("SBML FBC to COBRA Converter")
71 {
72
73 }
74
75
FbcToCobraConverter(const FbcToCobraConverter & orig)76 FbcToCobraConverter::FbcToCobraConverter(const FbcToCobraConverter& orig) :
77 SBMLConverter(orig)
78 {
79 }
80
81 FbcToCobraConverter*
clone() const82 FbcToCobraConverter::clone() const
83 {
84 return new FbcToCobraConverter(*this);
85 }
86
87
88 /*
89 * Destroy this object.
90 */
~FbcToCobraConverter()91 FbcToCobraConverter::~FbcToCobraConverter ()
92 {
93 }
94
95
96 ConversionProperties
getDefaultProperties() const97 FbcToCobraConverter::getDefaultProperties() const
98 {
99 static ConversionProperties prop;
100 prop.addOption("convert fbc to cobra", true, "convert FBC L3V1 to SBML L2V4 with COBRA annotation");
101 prop.addOption("overwriteReactionNotes", false, "write gene association into reaction notes, even if the reaction has notes already");
102 return prop;
103 }
104
105
106 bool
matchesProperties(const ConversionProperties & props) const107 FbcToCobraConverter::matchesProperties(const ConversionProperties &props) const
108 {
109 if (!props.hasOption("convert fbc to cobra"))
110 return false;
111 return true;
112 }
113
114
setObjectiveCoefficient(FbcModelPlugin * plugin,Model * model)115 void setObjectiveCoefficient(FbcModelPlugin* plugin, Model* model)
116 {
117 if (plugin == NULL || model == NULL)
118 return;
119
120 Objective* obj = plugin->getActiveObjective();
121 if (obj == NULL)
122 return;
123
124 for (unsigned int i = 0; i < obj->getNumFluxObjectives(); ++i)
125 {
126 FluxObjective* fluxObj = obj->getFluxObjective(i);
127 if (fluxObj == NULL)
128 continue;
129 Reaction* reaction = model->getReaction(fluxObj->getReaction());
130 if (reaction == NULL)
131 continue;
132 KineticLaw* law = reaction->getKineticLaw();
133 if (law == NULL)
134 continue;
135 LocalParameter* param = law->getLocalParameter("OBJECTIVE_COEFFICIENT");
136 param->setValue(fluxObj->getCoefficient());
137 }
138 }
139
140
createKineticLawForReaction(Reaction * reaction)141 void createKineticLawForReaction(Reaction* reaction)
142 {
143 if (reaction == NULL)
144 return;
145 reaction->unsetKineticLaw();
146 KineticLaw *law = reaction->getKineticLaw();
147 if (law == NULL)
148 {
149 law = reaction->createKineticLaw();
150 LocalParameter* fluxValue = law->createLocalParameter();
151 fluxValue->initDefaults();
152 fluxValue->setId("FLUX_VALUE");
153 fluxValue->setValue(0);
154 fluxValue->setUnits("dimensionless");
155 ASTNode* astn = SBML_parseFormula("FLUX_VALUE");
156 law->setMath(astn);
157 delete astn;
158 }
159
160 LocalParameter* LB = law->getLocalParameter("LOWER_BOUND");
161 if (LB == NULL)
162 {
163 LB = law->createLocalParameter();
164 LB->initDefaults();
165 LB->setId("LOWER_BOUND");
166 LB->setUnits("dimensionless");
167 LB->setValue(-std::numeric_limits<double>::infinity());
168 }
169
170 LocalParameter* UB = law->getLocalParameter("UPPER_BOUND");
171 if (UB == NULL)
172 {
173 UB = law->createLocalParameter();
174 UB->initDefaults();
175 UB->setId("UPPER_BOUND");
176 UB->setUnits("dimensionless");
177 LB->setValue(std::numeric_limits<double>::infinity());
178 }
179
180 LocalParameter* param = law->getLocalParameter("OBJECTIVE_COEFFICIENT");
181 if (param == NULL)
182 {
183 param = law->createLocalParameter();
184 param->initDefaults();
185 param->setId("OBJECTIVE_COEFFICIENT");
186 param->setUnits("dimensionless");
187 param->setValue(0);
188 }
189
190 }
191
updateKineticLawFromBound(Reaction * reaction,FluxBound * current)192 void updateKineticLawFromBound(Reaction* reaction, FluxBound* current)
193 {
194 if (reaction == NULL || current == NULL)
195 return;
196 const string operation = current -> getOperation();
197
198 KineticLaw *law = reaction->getKineticLaw();
199 LocalParameter* LB = law->getLocalParameter("LOWER_BOUND");
200 LocalParameter* UB = law->getLocalParameter("UPPER_BOUND");
201
202 if (operation == "less" || operation == "lessEqual" || operation == "equal")
203 {
204 UB->setValue(current->getValue());
205 }
206 if (operation == "greater" || operation == "greaterEqual" || operation == "equal")
207 {
208 LB->setValue(current->getValue());
209 }
210
211 }
212
getNotesForFormula(const string & formula)213 string getNotesForFormula(const string& formula)
214 {
215 stringstream str;
216
217 str
218 << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\t<p>FORMULA: "
219 << formula
220 << "</p>\n</html>";
221
222 return str.str();
223 }
224
getGeneAssociationForReaction(const FbcModelPlugin * plugin,const std::string & id)225 const GeneAssociation* getGeneAssociationForReaction(const FbcModelPlugin* plugin, const std::string& id)
226 {
227 if (plugin == NULL) return NULL;
228
229 for (int i = 0; i < plugin->getNumGeneAssociations(); ++i)
230 {
231 const GeneAssociation* association = plugin->getGeneAssociation((unsigned int)i);
232 if (association == NULL) continue;
233
234 if (association->isSetReaction() && association->getReaction() == id)
235 return association;
236 }
237 return NULL;
238 }
239
240 int
convert()241 FbcToCobraConverter::convert()
242 {
243 int result = LIBSBML_OPERATION_FAILED;
244
245 if (mDocument == NULL)
246 {
247 return LIBSBML_INVALID_OBJECT;
248 }
249
250 Model* mModel = mDocument->getModel();
251 if (mModel == NULL)
252 {
253 return LIBSBML_INVALID_OBJECT;
254 }
255
256 FbcModelPlugin *plugin =
257 static_cast<FbcModelPlugin*>(mDocument->getModel()->getPlugin("fbc"));
258
259 // if we have don't have a fbc model we cannot do the conversion
260 if (plugin == NULL || mDocument->getLevel() != 3)
261 {
262 return LIBSBML_OPERATION_FAILED;
263 }
264
265 plugin->unsetStrict();
266 // collect information
267
268 Model* model = mDocument->getModel();
269 map<const string, int> chargeMap;
270 map<const string, string> formulaMap;
271
272 for (unsigned int i = 0; i < model->getNumSpecies(); ++i)
273 {
274 Species* current = model->getSpecies(i);
275 const string& currentId = current->getId();
276 FbcSpeciesPlugin *splugin = static_cast<FbcSpeciesPlugin*>(current->getPlugin("fbc"));
277 if (splugin == NULL)
278 continue;
279 if (splugin->isSetCharge())
280 {
281 chargeMap[currentId] = splugin->getCharge();
282 }
283 if (splugin->isSetChemicalFormula())
284 {
285 formulaMap[currentId] = splugin->getChemicalFormula();
286 }
287 }
288
289
290 // overwrite reactionNotes
291 bool overwriteReactionNotes = getProperties() != NULL &&
292 getProperties()->hasOption("overwriteReactionNotes") &&
293 getProperties()->getBoolValue("overwriteReactionNotes");
294
295 // create KineticLaw
296 for (unsigned int i = 0; i < model->getNumReactions(); ++i)
297 {
298 Reaction* reaction = model->getReaction(i);
299 if (reaction == NULL)
300 continue;
301
302 createKineticLawForReaction(reaction);
303
304 FbcReactionPlugin* rplug = dynamic_cast<FbcReactionPlugin*>(reaction->getPlugin("fbc"));
305
306 // get gene association for reaction
307 const GeneAssociation* association = getGeneAssociationForReaction(plugin, reaction->getId());
308 std::string infix;
309
310 if (association != NULL && association->getAssociation() != NULL)
311 {
312 infix = association->getAssociation()->toInfix();
313 }
314 else if (rplug != NULL && rplug->isSetGeneProductAssociation())
315 {
316 infix = rplug->getGeneProductAssociation()->getAssociation()->toInfix();
317 rplug->unsetGeneProductAssociation();
318 }
319
320 if (infix.empty()) continue;
321
322 //unset notes if requested
323 if (reaction->isSetNotes() && overwriteReactionNotes)
324 reaction->unsetNotes();
325
326 // skip adding gene association if we already have notes
327 if (reaction->isSetNotes()) continue;
328
329
330
331 // write new notes
332 reaction->setNotes(
333 "<body xmlns='http://www.w3.org/1999/xhtml'>\n"
334 " <p>GENE_ASSOCIATION : " + infix +"</p>\n"
335 "</body>"
336 );
337
338 if (rplug != NULL)
339 {
340 rplug->unsetLowerFluxBound();
341 rplug->unsetUpperFluxBound();
342 }
343
344 }
345
346 // update kinetic law from bounds
347 for (unsigned int i = 0; i < plugin->getNumFluxBounds(); ++i)
348 {
349 FluxBound *current = plugin->getFluxBound(i);
350 if (current == NULL)
351 continue;
352 Reaction* reaction = model->getReaction(current->getReaction());
353 if (reaction == NULL)
354 continue;
355
356 updateKineticLawFromBound(reaction, current);
357
358 }
359
360 setObjectiveCoefficient(plugin, model);
361
362 // disable package
363 mDocument->enablePackage("http://www.sbml.org/sbml/level3/version1/fbc/version1", "fbc",false);
364 mDocument->enablePackage("http://www.sbml.org/sbml/level3/version1/fbc/version2", "fbc",false);
365
366 // convert model to L2V1 (as L2V2 is the last model that had charge)
367 mDocument->setConversionValidators(AllChecksON & UnitsCheckOFF);
368
369 SBMLNamespaces sbmlns21(2,1);
370 ConversionProperties prop(&sbmlns21);
371 prop.addOption("strict", false, "should validity be preserved");
372 prop.addOption("ignorePackages", true, "convert even if packages are used");
373 prop.addOption("setLevelAndVersion", true, "convert the document to the given level and version");
374 int conversionResult = mDocument->convert(prop);
375 if (conversionResult != LIBSBML_OPERATION_SUCCESS)
376 return conversionResult;
377
378 // set charge on species
379 for (unsigned int i = 0; i < model->getNumSpecies(); ++i)
380 {
381 Species* current = model->getSpecies(i);
382 const string currentId = current->getId();
383 int charge = chargeMap[currentId];
384
385 if (charge != 0)
386 current->setCharge(charge);
387
388 const string formula = formulaMap[currentId];
389 if (!formula.empty())
390 {
391 current->setNotes( getNotesForFormula(formula) );
392 }
393 }
394
395
396 result = LIBSBML_OPERATION_SUCCESS;
397 return result;
398 }
399
400
401 /** @cond doxygenIgnored */
402 /** @endcond */
403
404 LIBSBML_CPP_NAMESPACE_END
405
406 #endif /* __cplusplus */
407
408
409