1use anyhow::Result;
2use pharmsol::equation::Equation;
3use pharmsol::{Analytical, ValidatedModelMetadata, ODE, SDE};
4
5pub mod metadata;
6pub mod parameter_space;
7
8pub use metadata::ModelMetadata;
9pub use parameter_space::{
10 BoundedParameter, Parameter, ParameterMeta, ParameterScale, ParameterSpace, UnboundedParameter,
11};
12
13#[derive(Debug, Clone)]
14pub struct Model<E: Equation> {
15 pub equation: E,
16}
17
18impl<E: Equation> Model<E> {
19 pub fn builder(equation: E) -> ModelBuilder<E> {
20 ModelBuilder { equation }
21 }
22}
23
24impl<E: EquationMetadataSource> Model<E> {
25 pub fn parameter_count(&self) -> usize {
26 self.equation
27 .equation_metadata()
28 .map_or(0, |metadata| metadata.parameters().len())
29 }
30
31 pub fn parameter_name(&self, index: usize) -> Option<&str> {
32 self.equation
33 .equation_metadata()
34 .and_then(|metadata| metadata.parameters().get(index))
35 .map(|parameter| parameter.name())
36 }
37
38 pub fn parameter_index(&self, name: &str) -> Option<usize> {
39 self.equation
40 .equation_metadata()
41 .and_then(|metadata| metadata.parameter_index(name))
42 }
43
44 pub fn output_count(&self) -> usize {
45 self.equation
46 .equation_metadata()
47 .map_or(0, |metadata| metadata.outputs().len())
48 }
49
50 pub fn output_name(&self, outeq: usize) -> Option<&str> {
51 self.equation
52 .equation_metadata()
53 .and_then(|metadata| metadata.outputs().get(outeq))
54 .map(|output| output.name())
55 }
56
57 pub fn output_index(&self, name: &str) -> Option<usize> {
58 self.equation.equation_metadata().and_then(|metadata| {
59 metadata
60 .outputs()
61 .iter()
62 .position(|output| output.name() == name)
63 })
64 }
65}
66
67pub struct ModelBuilder<E: Equation> {
68 equation: E,
69}
70
71impl<E: Equation> ModelBuilder<E> {
72 pub fn build(self) -> Result<Model<E>> {
73 Ok(Model {
74 equation: self.equation,
75 })
76 }
77}
78
79impl<E: EquationMetadataSource> ModelBuilder<E> {
80 pub(crate) fn parameter_index(&self, name: &str) -> Option<usize> {
81 self.equation
82 .equation_metadata()
83 .and_then(|metadata| metadata.parameter_index(name))
84 }
85
86 pub(crate) fn parameter_names(&self) -> Vec<String> {
87 self.equation
88 .equation_metadata()
89 .map(|metadata| {
90 metadata
91 .parameters()
92 .iter()
93 .map(|parameter| parameter.name().to_string())
94 .collect()
95 })
96 .unwrap_or_default()
97 }
98
99 pub(crate) fn output_names(&self) -> Vec<String> {
100 self.equation
101 .equation_metadata()
102 .map(|metadata| {
103 metadata
104 .outputs()
105 .iter()
106 .map(|output| output.name().to_string())
107 .collect()
108 })
109 .unwrap_or_default()
110 }
111
112 pub(crate) fn output_index(&self, name: &str) -> Option<usize> {
113 self.equation.equation_metadata().and_then(|metadata| {
114 metadata
115 .outputs()
116 .iter()
117 .position(|output| output.name() == name)
118 })
119 }
120}
121
122pub trait EquationMetadataSource: Equation {
123 fn equation_metadata(&self) -> Option<&ValidatedModelMetadata>;
124}
125
126macro_rules! impl_metadata_opt {
128 ($($t:ty),+) => {
129 $(impl EquationMetadataSource for $t {
130 fn equation_metadata(&self) -> Option<&ValidatedModelMetadata> {
131 self.metadata()
132 }
133 })+
134 };
135}
136impl_metadata_opt!(ODE, Analytical, SDE);
137
138macro_rules! impl_metadata_some {
140 ($($t:ty),+) => {
141 $(
142 #[cfg(any(
143 feature = "dsl-jit",
144 all(feature = "dsl-aot", feature = "dsl-aot-load"),
145 all(feature = "dsl-wasm", not(all(target_arch = "wasm32", target_os = "unknown")))
146 ))]
147 impl EquationMetadataSource for $t {
148 fn equation_metadata(&self) -> Option<&ValidatedModelMetadata> {
149 Some(self.metadata())
150 }
151 }
152 )+
153 };
154}
155impl_metadata_some!(
156 pharmsol::dsl::RuntimeOdeModel,
157 pharmsol::dsl::RuntimeAnalyticalModel,
158 pharmsol::dsl::RuntimeSdeModel
159);