1 /**
2  * @file    SBMLInferUnitsConverter.cpp
3  * @brief   Implementation of SBMLInferUnitsConverter.
4  * @author  Sarah Keating
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  * Copyright (C) 2006-2008 by the California Institute of Technology,
29  *     Pasadena, CA, USA
30  *
31  * Copyright (C) 2002-2005 jointly by the following organizations:
32  *     1. California Institute of Technology, Pasadena, CA, USA
33  *     2. Japan Science and Technology Agency, Japan
34  *
35  * This library is free software; you can redistribute it and/or modify it
36  * under the terms of the GNU Lesser General Public License as published by
37  * the Free Software Foundation.  A copy of the license agreement is provided
38  * in the file named "LICENSE.txt" included with this software distribution
39  * and also available online as http://sbml.org/software/libsbml/license.html
40  * ------------------------------------------------------------------------ -->
41  */
42 
43 #include <sbml/conversion/SBMLInferUnitsConverter.h>
44 #include <sbml/conversion/SBMLConverterRegistry.h>
45 #include <sbml/conversion/SBMLConverterRegister.h>
46 #include <sbml/SBMLWriter.h>
47 #include <sbml/SBMLReader.h>
48 #include <sbml/SBMLDocument.h>
49 #include <sbml/Model.h>
50 
51 #ifdef __cplusplus
52 
53 #include <algorithm>
54 #include <string>
55 
56 using namespace std;
57 LIBSBML_CPP_NAMESPACE_BEGIN
58 
59 
60 /** @cond doxygenLibsbmlInternal */
init()61 void SBMLInferUnitsConverter::init()
62 {
63   SBMLInferUnitsConverter converter;
64   SBMLConverterRegistry::getInstance().addConverter(&converter);
65 }
66 /** @endcond */
67 
68 
SBMLInferUnitsConverter()69 SBMLInferUnitsConverter::SBMLInferUnitsConverter ()
70   : SBMLConverter("SBML Infer Units Converter")
71 {
72   newIdCount = 0;
73 }
74 
75 
76 /*
77  * Copy constructor.
78  */
SBMLInferUnitsConverter(const SBMLInferUnitsConverter & orig)79 SBMLInferUnitsConverter::SBMLInferUnitsConverter(const SBMLInferUnitsConverter& orig) :
80     SBMLConverter(orig)
81 {
82   newIdCount = orig.newIdCount;
83 }
84 
85 
86 /*
87  * Destroy this object.
88  */
~SBMLInferUnitsConverter()89 SBMLInferUnitsConverter::~SBMLInferUnitsConverter ()
90 {
91 }
92 
93 
94 /*
95  * Assignment operator for SBMLInferUnitsConverter.
96  */
97 SBMLInferUnitsConverter&
operator =(const SBMLInferUnitsConverter & rhs)98 SBMLInferUnitsConverter::operator=(const SBMLInferUnitsConverter& rhs)
99 {
100   if(&rhs!=this)
101   {
102     this->SBMLConverter::operator =(rhs);
103   }
104 
105   return *this;
106 }
107 
108 
109 SBMLInferUnitsConverter*
clone() const110 SBMLInferUnitsConverter::clone () const
111 {
112   return new SBMLInferUnitsConverter(*this);
113 }
114 
115 
116 ConversionProperties
getDefaultProperties() const117 SBMLInferUnitsConverter::getDefaultProperties() const
118 {
119   static ConversionProperties prop;
120   static bool init = false;
121 
122   if (init)
123   {
124     return prop;
125   }
126   else
127   {
128     prop.addOption("inferUnits", true,
129                    "Infer the units of Parameters");
130     init = true;
131     return prop;
132   }
133 }
134 
135 
136 bool
matchesProperties(const ConversionProperties & props) const137 SBMLInferUnitsConverter::matchesProperties(const ConversionProperties &props) const
138 {
139   if (!props.hasOption("inferUnits"))
140     return false;
141   return true;
142 }
143 
144 
145 int
convert()146 SBMLInferUnitsConverter::convert()
147 {
148   if (mDocument == NULL)
149   {
150     return LIBSBML_OPERATION_FAILED;
151   }
152 
153   Model* mModel = mDocument->getModel();
154   if (mModel == NULL)
155   {
156     return LIBSBML_INVALID_OBJECT;
157   }
158 
159   /* check consistency of model */
160   /* since this function will write to the error log we should
161   * clear anything in the log first
162   */
163   mDocument->getErrorLog()->clearLog();
164   unsigned char origValidators = mDocument->getApplicableValidators();
165 
166   mDocument->setApplicableValidators(AllChecksON);
167 
168   mDocument->checkConsistency();
169 
170 
171   /* replace original consistency checks */
172   mDocument->setApplicableValidators(origValidators);
173 
174   if (mDocument->getErrorLog()->getNumFailsWithSeverity(LIBSBML_SEV_ERROR) != 0)
175   {
176     return LIBSBML_CONV_INVALID_SRC_DOCUMENT;
177   }
178 
179   /* so we have a consistent model - we can try inferring units */
180   // TO DO keep a copy of model so we can revert back to it if things go wrong
181   std::string newId;
182   char number[4];
183   for (unsigned int i = 0; i < mModel->getNumParameters(); i++)
184   {
185     if (mModel->getParameter(i)->isSetUnits() == false)
186     {
187       UnitDefinition * inferred = NULL;
188       mModel->getParameter(i)->setCalculatingUnits(true);
189       inferred = mModel->getParameter(i)->getDerivedUnitDefinition();
190       mModel->getParameter(i)->setCalculatingUnits(false);
191 
192       if (inferred != NULL && inferred->getNumUnits() != 0)
193       {
194         bool baseUnit = false;
195 
196         newId = existsAlready(*(mModel), inferred);
197 
198         if (newId.empty())
199         {
200           if (inferred->isVariantOfDimensionless())
201           {
202             newId = "dimensionless";
203             baseUnit = true;
204           }
205           else if (inferred->getNumUnits() == 1)
206           {
207             Unit * u = inferred->getUnit(0);
208             Unit * defaultU = new Unit(u->getSBMLNamespaces());
209             defaultU->initDefaults();
210             defaultU->setKind(u->getKind());
211             if (Unit::areIdentical(u, defaultU) == true)
212             {
213               newId = UnitKind_toString(u->getKind());
214               baseUnit = true;
215             }
216             delete defaultU;
217             defaultU = NULL;
218           }
219         }
220 
221         if (newId.empty())
222         {
223           /* create an id for the unitDef */
224           sprintf(number, "%u", newIdCount);
225           newId = "unitSid_" + string(number);
226           newIdCount++;
227 
228           /* double check that this id has not been used */
229           while (mModel->getUnitDefinition(newId) != NULL)
230           {
231             sprintf(number, "%u", newIdCount);
232             newId = "unitSid_" + string(number);
233             newIdCount++;
234           }
235         }
236 
237         if (baseUnit == false)
238         {
239           inferred->setId(newId);
240           mModel->addUnitDefinition(inferred);
241         }
242 
243         mModel->getParameter(i)->setUnits(newId);
244 
245         delete inferred;
246         inferred = NULL;
247       }
248     }
249   }
250 
251   return LIBSBML_OPERATION_SUCCESS;
252 }
253 
254 
255 
256 
257 
258 /** @cond doxygenLibsbmlInternal */
259 std::string
existsAlready(Model & m,UnitDefinition * newUD)260 SBMLInferUnitsConverter::existsAlready(Model& m, UnitDefinition *newUD)
261 {
262   std::string id = "";
263   for (unsigned int i = 0; i < m.getNumUnitDefinitions(); i++)
264   {
265     if (UnitDefinition::areIdentical(m.getUnitDefinition(i), newUD))
266     {
267       return m.getUnitDefinition(i)->getId();
268     }
269   }
270 
271   return id;
272 }
273 /** @endcond */
274 
275 
276 
277 /** @cond doxygenIgnored */
278 /** @endcond */
279 
280 LIBSBML_CPP_NAMESPACE_END
281 
282 #endif  /* __cplusplus */
283 
284 
285