Skip to content

Commit aa899a7

Browse files
committed
Store problem configuration in v2.Problem
Introduces `v2.Problem.config` which contains the info from the PEtab yaml file. The same as PEtab-dev#326, but for `v2.Problem`.
1 parent 4a551a7 commit aa899a7

File tree

1 file changed

+70
-29
lines changed

1 file changed

+70
-29
lines changed

petab/v2/problem.py

Lines changed: 70 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from typing import TYPE_CHECKING
1313

1414
import pandas as pd
15+
from pydantic import AnyUrl, BaseModel, Field
1516

1617
from ..v1 import (
1718
conditions,
@@ -25,6 +26,7 @@
2526
yaml,
2627
)
2728
from ..v1.models.model import Model, model_factory
29+
from ..v1.problem import ListOfFiles, VersionNumber
2830
from ..v1.yaml import get_path_prefix
2931
from ..v2.C import * # noqa: F403
3032
from . import experiments
@@ -73,6 +75,7 @@ def __init__(
7375
observable_df: pd.DataFrame = None,
7476
mapping_df: pd.DataFrame = None,
7577
extensions_config: dict = None,
78+
config: ProblemConfig = None,
7679
):
7780
from ..v2.lint import default_validation_tasks
7881

@@ -88,7 +91,7 @@ def __init__(
8891
self.validation_tasks: list[
8992
ValidationTask
9093
] = default_validation_tasks.copy()
91-
94+
self.config = config
9295
if self.experiment_df is not None:
9396
warnings.warn(
9497
"The experiment table is not yet supported and "
@@ -199,40 +202,38 @@ def get_path(filename):
199202
"Consider using "
200203
"petab.CompositeProblem.from_yaml() instead."
201204
)
205+
config = ProblemConfig(
206+
**yaml_config, base_path=base_path, filepath=yaml_file
207+
)
208+
problem0 = config.problems[0]
202209

203-
problem0 = yaml_config["problems"][0]
204-
205-
if isinstance(yaml_config[PARAMETER_FILE], list):
210+
if isinstance(config.parameter_file, list):
206211
parameter_df = parameters.get_parameter_df(
207-
[get_path(f) for f in yaml_config[PARAMETER_FILE]]
212+
[get_path(f) for f in config.parameter_file]
208213
)
209214
else:
210215
parameter_df = (
211-
parameters.get_parameter_df(
212-
get_path(yaml_config[PARAMETER_FILE])
213-
)
214-
if yaml_config[PARAMETER_FILE]
216+
parameters.get_parameter_df(get_path(config.parameter_file))
217+
if config.parameter_file
215218
else None
216219
)
217220

218-
if len(problem0[MODEL_FILES] or []) > 1:
221+
if len(problem0.model_files or []) > 1:
219222
# TODO https://github.com/PEtab-dev/libpetab-python/issues/6
220223
raise NotImplementedError(
221224
"Support for multiple models is not yet implemented."
222225
)
223-
if not problem0[MODEL_FILES]:
226+
if not problem0.model_files:
224227
model = None
225228
else:
226-
model_id, model_info = next(iter(problem0[MODEL_FILES].items()))
229+
model_id, model_info = next(iter(problem0.model_files.items()))
227230
model = model_factory(
228-
get_path(model_info[MODEL_LOCATION]),
229-
model_info[MODEL_LANGUAGE],
231+
get_path(model_info.location),
232+
model_info.language,
230233
model_id=model_id,
231234
)
232235

233-
measurement_files = [
234-
get_path(f) for f in problem0.get(MEASUREMENT_FILES, [])
235-
]
236+
measurement_files = [get_path(f) for f in problem0.measurement_files]
236237
# If there are multiple tables, we will merge them
237238
measurement_df = (
238239
core.concat_tables(
@@ -242,19 +243,15 @@ def get_path(filename):
242243
else None
243244
)
244245

245-
condition_files = [
246-
get_path(f) for f in problem0.get(CONDITION_FILES, [])
247-
]
246+
condition_files = [get_path(f) for f in problem0.condition_files]
248247
# If there are multiple tables, we will merge them
249248
condition_df = (
250249
core.concat_tables(condition_files, conditions.get_condition_df)
251250
if condition_files
252251
else None
253252
)
254253

255-
experiment_files = [
256-
get_path(f) for f in problem0.get(EXPERIMENT_FILES, [])
257-
]
254+
experiment_files = [get_path(f) for f in problem0.experiment_files]
258255
# If there are multiple tables, we will merge them
259256
experiment_df = (
260257
core.concat_tables(experiment_files, experiments.get_experiment_df)
@@ -263,7 +260,7 @@ def get_path(filename):
263260
)
264261

265262
visualization_files = [
266-
get_path(f) for f in problem0.get(VISUALIZATION_FILES, [])
263+
get_path(f) for f in problem0.visualization_files
267264
]
268265
# If there are multiple tables, we will merge them
269266
visualization_df = (
@@ -272,17 +269,15 @@ def get_path(filename):
272269
else None
273270
)
274271

275-
observable_files = [
276-
get_path(f) for f in problem0.get(OBSERVABLE_FILES, [])
277-
]
272+
observable_files = [get_path(f) for f in problem0.observable_files]
278273
# If there are multiple tables, we will merge them
279274
observable_df = (
280275
core.concat_tables(observable_files, observables.get_observable_df)
281276
if observable_files
282277
else None
283278
)
284279

285-
mapping_files = [get_path(f) for f in problem0.get(MAPPING_FILES, [])]
280+
mapping_files = [get_path(f) for f in problem0.mapping_files]
286281
# If there are multiple tables, we will merge them
287282
mapping_df = (
288283
core.concat_tables(mapping_files, mapping.get_mapping_df)
@@ -299,7 +294,7 @@ def get_path(filename):
299294
model=model,
300295
visualization_df=visualization_df,
301296
mapping_df=mapping_df,
302-
extensions_config=yaml_config.get(EXTENSIONS, {}),
297+
extensions_config=config.extensions,
303298
)
304299

305300
@staticmethod
@@ -981,3 +976,49 @@ def add_experiment(self, id_: str, *args):
981976
if self.experiment_df is not None
982977
else tmp_df
983978
)
979+
980+
981+
class ModelFile(BaseModel):
982+
"""A file in the PEtab problem configuration."""
983+
984+
location: str | AnyUrl
985+
language: str
986+
987+
988+
class SubProblem(BaseModel):
989+
"""A `problems` object in the PEtab problem configuration."""
990+
991+
model_files: dict[str, ModelFile] | None = {}
992+
measurement_files: ListOfFiles = []
993+
condition_files: ListOfFiles = []
994+
experiment_files: ListOfFiles = []
995+
observable_files: ListOfFiles = []
996+
visualization_files: ListOfFiles = []
997+
mapping_files: ListOfFiles = []
998+
999+
1000+
class ExtensionConfig(BaseModel):
1001+
"""The configuration of a PEtab extension."""
1002+
1003+
name: str
1004+
version: str
1005+
config: dict
1006+
1007+
1008+
class ProblemConfig(BaseModel):
1009+
"""The PEtab problem configuration."""
1010+
1011+
filepath: str | AnyUrl | None = Field(
1012+
None,
1013+
description="The path to the PEtab problem configuration.",
1014+
exclude=True,
1015+
)
1016+
base_path: str | AnyUrl | None = Field(
1017+
None,
1018+
description="The base path to resolve relative paths.",
1019+
exclude=True,
1020+
)
1021+
format_version: VersionNumber = "2.0.0"
1022+
parameter_file: str | AnyUrl | None = None
1023+
problems: list[SubProblem] = []
1024+
extensions: list[ExtensionConfig] = []

0 commit comments

Comments
 (0)