11"""Tools and helpers to handle parameters for sensitivity analysis."""
2+ from __future__ import annotations
3+
4+ from enum import Enum
25from pathlib import Path
3- from typing import Optional
4- from dataclasses import dataclass
6+ from typing import Optional , Iterable , Tuple
57
68import libsbml
79import numpy as np
810from sbmlutils .console import console
911from sbmlutils .factory import ValueWithUnit
12+ from pydantic import BaseModel , Field , validator , ConfigDict
13+ import pandas as pd
14+ from sbmlutils .report .units import udef_to_string
1015
1116import roadrunner
1217
18+ class ParameterType (str , Enum ):
19+ NA = "NA"
20+ FIT = "fitted"
21+ SCALING = "scaling"
22+ DATA = "data"
23+
24+
25+ class SensitivityParameter (BaseModel ):
26+ """Parameter for SensitivityAnalysis."""
27+ model_config = ConfigDict (use_enum_values = True )
1328
14- @dataclass
15- class SensitivityParameter :
16- """Parameter for SensitivityAnalysis"""
1729 uid : str
1830 name : str
31+ value : float = Field (default = np .nan )
32+ lower_bound : float = Field (default = np .nan )
33+ upper_bound : float = Field (default = np .nan )
1934 unit : Optional [str ] = None
20- lower_bound : float = np .nan
21- upper_bound : float = np .nan
35+ type : ParameterType = ParameterType .NA
2236
2337 def __hash__ (self ):
2438 return hash (self .uid )
2539
40+ @staticmethod
41+ def parameters_to_df (parameters : Iterable [SensitivityParameter ]) -> pd .DataFrame :
42+ """Create parameter table from parameters."""
43+ items = []
44+ for item in parameters :
45+ items .append (item .model_dump ())
46+ console .print (items )
47+ df = pd .DataFrame (items )
48+ return df
49+
50+ @staticmethod
51+ def parameters_set_bounds (parameters : Iterable [SensitivityParameter ], bounds : Iterable [tuple ]) -> None :
52+ """Set bounds for sensitivity analysis."""
2653
54+ parameters_d = {p .uid : p for p in parameters }
55+
56+ for (key , lb , ub , ptype ) in bounds :
57+ if key not in parameters_d :
58+ console .print (f"unused bounds definition: { key } = [{ lb } , { ub } ]" )
59+ else :
60+ p = parameters_d [key ]
61+ p .lower_bound = lb
62+ p .upper_bound = ub
63+ p .type = ptype
2764
2865
2966def parameters_for_sensitivity_analysis (
@@ -41,22 +78,43 @@ def parameters_for_sensitivity_analysis(
4178 :param exclude_na: whether to exclude NA values
4279 :return: dict[id, name]
4380 """
81+ r : roadrunner .RoadRunner = roadrunner .RoadRunner (str (sbml_path ))
82+ doc : libsbml .SBMLDocument = libsbml .readSBMLFromFile (str (sbml_path ))
83+ sbml_model : libsbml .Model = doc .getModel ()
84+
4485
4586 def parameter_from_sbase (sbase : libsbml .SBase ) -> SensitivityParameter :
4687 """Create parameter from SBase for sensitivity analysis."""
4788 uid = sbase .getId ()
89+
4890 name = sbase .getName () if sbase .isSetName () else uid
4991 udef : libsbml .UnitDefinition = sbase .getDerivedUnitDefinition ()
5092 unit : str = libsbml .UnitDefinition .printUnits (ud = udef , compact = True )
93+ unit_str : str = udef_to_string (udef , model = None , format = "str" )
94+
95+ # handle the species concentration
96+ typecode = sbase .getTypeCode ()
97+ if typecode == libsbml .SpeciesType :
98+ s : libsbml .Species = sbase
99+ if s .getHasOnlySubstanceUnits ():
100+ value = r .getValue (f"[{ uid } ]" )
101+ else :
102+ value = r .getValue (uid )
103+ else :
104+ value = r .getValue (uid )
51105
52106 # FIXME: get bound information from SBML model or table
53- parameter = SensitivityParameter (uid = uid , name = name , unit = unit )
107+ parameter = SensitivityParameter (
108+ uid = uid ,
109+ name = name ,
110+ value = value ,
111+ unit = unit_str ,
112+ lower_bound = np .nan ,
113+ upper_bound = np .nan ,
114+ )
54115
55116 return parameter
56117
57- r : roadrunner .RoadRunner = roadrunner .RoadRunner (str (sbml_path ))
58- doc : libsbml .SBMLDocument = libsbml .readSBMLFromFile (str (sbml_path ))
59- sbml_model : libsbml .Model = doc .getModel ()
60118
61119 parameters = []
62120 excluded : list [SensitivityParameter ] = []
0 commit comments