|
1 | 1 | """Functions for interacting with SBML models""" |
2 | 2 | import logging |
3 | 3 | import re |
| 4 | +from numbers import Number |
4 | 5 | from pathlib import Path |
5 | | -from typing import Any, Dict, List, Tuple, Union |
| 6 | +from typing import Any, Dict, List, Optional, Tuple, Union |
6 | 7 | from warnings import warn |
7 | 8 |
|
8 | 9 | import libsbml |
9 | 10 | from pandas.io.common import get_handle, is_file_like, is_url |
10 | 11 |
|
| 12 | +import petab |
| 13 | + |
11 | 14 | logger = logging.getLogger(__name__) |
12 | 15 | __all__ = ['add_global_parameter', |
13 | 16 | 'add_model_output', |
14 | 17 | 'add_model_output_sigma', |
15 | 18 | 'add_model_output_with_sigma', |
16 | 19 | 'assignment_rules_to_dict', |
17 | 20 | 'create_assigment_rule', |
| 21 | + 'get_model_for_condition', |
18 | 22 | 'get_model_parameters', |
19 | 23 | 'get_observables', |
20 | 24 | 'get_sbml_model', |
@@ -481,3 +485,118 @@ def load_sbml_from_file( |
481 | 485 | sbml_model = sbml_document.getModel() |
482 | 486 |
|
483 | 487 | return sbml_reader, sbml_document, sbml_model |
| 488 | + |
| 489 | + |
| 490 | +def get_model_for_condition( |
| 491 | + petab_problem: "petab.Problem", |
| 492 | + sim_condition_id: str = None, |
| 493 | + preeq_condition_id: Optional[str] = None, |
| 494 | +) -> Tuple[libsbml.SBMLDocument, libsbml.Model]: |
| 495 | + """Create an SBML model for the given condition. |
| 496 | +
|
| 497 | + Creates a copy of the model and updates parameters according to the PEtab |
| 498 | + files. Estimated parameters are set to their ``nominalValue``. |
| 499 | + Observables defined in the observables table are not added to the model. |
| 500 | +
|
| 501 | + :param petab_problem: PEtab problem |
| 502 | + :param sim_condition_id: Simulation ``conditionId`` for which to generate a |
| 503 | + model |
| 504 | + :param preeq_condition_id: Preequilibration ``conditionId`` of the settings |
| 505 | + for which to generate a model. This is only used to determine the |
| 506 | + relevant output parameter overrides. Preequilibration is not encoded |
| 507 | + in the resulting model. |
| 508 | + :return: The generated SBML document, and SBML model |
| 509 | + """ |
| 510 | + condition_dict = {petab.SIMULATION_CONDITION_ID: sim_condition_id} |
| 511 | + if preeq_condition_id: |
| 512 | + condition_dict[petab.PREEQUILIBRATION_CONDITION_ID] = \ |
| 513 | + preeq_condition_id |
| 514 | + cur_measurement_df = petab.measurements.get_rows_for_condition( |
| 515 | + measurement_df=petab_problem.measurement_df, |
| 516 | + condition=condition_dict, |
| 517 | + ) |
| 518 | + parameter_map, scale_map = \ |
| 519 | + petab.parameter_mapping.get_parameter_mapping_for_condition( |
| 520 | + condition_id=sim_condition_id, |
| 521 | + is_preeq=False, |
| 522 | + cur_measurement_df=cur_measurement_df, |
| 523 | + sbml_model=petab_problem.sbml_model, |
| 524 | + condition_df=petab_problem.condition_df, |
| 525 | + parameter_df=petab_problem.parameter_df, |
| 526 | + warn_unmapped=True, |
| 527 | + scaled_parameters=False, |
| 528 | + fill_fixed_parameters=True, |
| 529 | + # will only become problematic once the observable and noise terms |
| 530 | + # are added to the model |
| 531 | + allow_timepoint_specific_numeric_noise_parameters=True, |
| 532 | + ) |
| 533 | + # create a copy of the model |
| 534 | + sbml_doc = petab_problem.sbml_model.getSBMLDocument().clone() |
| 535 | + sbml_model = sbml_doc.getModel() |
| 536 | + |
| 537 | + # fill in parameters |
| 538 | + def get_param_value(parameter_id: str): |
| 539 | + """Parameter value from mapping or nominal value""" |
| 540 | + mapped_value = parameter_map.get(parameter_id) |
| 541 | + if not isinstance(mapped_value, str): |
| 542 | + return mapped_value |
| 543 | + |
| 544 | + # estimated parameter, look up in nominal parameters |
| 545 | + return petab_problem.parameter_df.loc[mapped_value, |
| 546 | + petab.NOMINAL_VALUE] |
| 547 | + |
| 548 | + def remove_rules(target_id: str): |
| 549 | + if sbml_model.removeRuleByVariable(target_id): |
| 550 | + warn("An SBML rule was removed to set the component " |
| 551 | + f"{target_id} to a constant value.") |
| 552 | + sbml_model.removeInitialAssignment(target_id) |
| 553 | + |
| 554 | + for parameter in sbml_model.getListOfParameters(): |
| 555 | + new_value = get_param_value(parameter.getId()) |
| 556 | + if new_value: |
| 557 | + parameter.setValue(new_value) |
| 558 | + # remove rules that would override that value |
| 559 | + remove_rules(parameter.getId()) |
| 560 | + |
| 561 | + # set concentrations for any overridden species |
| 562 | + for component_id in petab_problem.condition_df: |
| 563 | + sbml_species = sbml_model.getSpecies(component_id) |
| 564 | + if not sbml_species: |
| 565 | + continue |
| 566 | + |
| 567 | + # remove any rules overriding that species' initials |
| 568 | + remove_rules(component_id) |
| 569 | + |
| 570 | + # set initial concentration/amount |
| 571 | + new_value = petab.to_float_if_float( |
| 572 | + petab_problem.condition_df.loc[sim_condition_id, component_id]) |
| 573 | + if not isinstance(new_value, Number): |
| 574 | + # parameter reference in condition table |
| 575 | + new_value = get_param_value(new_value) |
| 576 | + |
| 577 | + if sbml_species.isSetInitialAmount() \ |
| 578 | + or (sbml_species.getHasOnlySubstanceUnits() |
| 579 | + and not sbml_species.isSetInitialConcentration()): |
| 580 | + sbml_species.setInitialAmount(new_value) |
| 581 | + else: |
| 582 | + sbml_species.setInitialConcentration(new_value) |
| 583 | + |
| 584 | + # set compartment size for any compartments in the condition table |
| 585 | + for component_id in petab_problem.condition_df: |
| 586 | + sbml_compartment = sbml_model.getCompartment(component_id) |
| 587 | + if not sbml_compartment: |
| 588 | + continue |
| 589 | + |
| 590 | + # remove any rules overriding that compartment's size |
| 591 | + remove_rules(component_id) |
| 592 | + |
| 593 | + # set initial concentration/amount |
| 594 | + new_value = petab.to_float_if_float( |
| 595 | + petab_problem.condition_df.loc[sim_condition_id, component_id]) |
| 596 | + if not isinstance(new_value, Number): |
| 597 | + # parameter reference in condition table |
| 598 | + new_value = get_param_value(new_value) |
| 599 | + |
| 600 | + sbml_compartment.setSize(new_value) |
| 601 | + |
| 602 | + return sbml_doc, sbml_model |
0 commit comments