Skip to content

Note

Click here to download the full example code

Manage model results

Manage model results using several result managers.

from __future__ import annotations

import logging

import mlflow
from gemseo.datasets.dataset import Dataset
from gemseo.post.dataset.bars import BarPlot
from mlflow import delete_run
from numpy import atleast_1d
from pandas import DataFrame
from pandas import concat

from vimseo import EXAMPLE_RUNS_DIR
from vimseo.api import activate_logger
from vimseo.api import create_model
from vimseo.core.model_result import ModelResult
from vimseo.core.model_settings import IntegratedModelSettings
from vimseo.storage_management.base_storage_manager import PersistencyPolicy
from vimseo.utilities.plotting_utils import plot_curves

activate_logger(level=logging.INFO)

Properly storing model results when running hundreds of simulations with possibly several users and several studies at the same time is a key aspect, especially if model credibility demonstration is targeted. Our strategy is to separate the following concepts: - scratch results: these are specific to models relying on external solvers. A scratch directory contains the data read or written by the solver. - archive results: these are the data we want to keep after the simulation. They comprise the input and output data, metadata and files generated in the scratch directory that we want to conserve. - a cache: while a cache could be seen as a way of archiving results, we prefer to separate cache and archiving. However, a bridge is available between both: a cache can be generated from an archive or part of an archive.

An archive manager can be chosen among DirectoryArchive or MlflowArchive. In the first case, an arborescence of directories is generated under a directory default_archive created in the current working directory. In the second case, an MLflow database is created. By default the database is file-based and is created under a directory mlflow_archive in the current working directory. The root directory of the archive can be specified with argument directory_archive_root passed to the constructor of the model or to create_model().

The scratch results are stored in an arborescence of directories similarly to a DirectoryArchive.

The persistency policy of the scratch and the archive can be specified independently.

By default, the storage generates unique directories at each new model execution. For a DirectoryArchive, the argument job_name allows to store the result in a specific directory without creating unique directories.

Here, a model is created with the default DirectoryArchive manager, and a specific job directory is used to store the archive result. The _accept_overwrite_job_dir attribute must be explicitly set to True. The MockModelPersistent model has the specificity of requiring to store some generated files to the archive. The archive manager automatically handles the copy of these files from the scratch to the archive. Here, the scratch directories are kept such that the user can look into them:

model_name = "MockModelPersistent"
load_case = "LC1"
model = create_model(
    model_name,
    load_case,
    IntegratedModelSettings(
        directory_scratch_persistency=PersistencyPolicy.DELETE_NEVER,
        directory_archive_root=EXAMPLE_RUNS_DIR / "archive/visualize_model_result",
        directory_scratch_root=EXAMPLE_RUNS_DIR / "scratch/visualize_model_result",
        cache_file_path=EXAMPLE_RUNS_DIR
        / f"caches/visualize_model_result/{model_name}_{load_case}_cache.hdf",
    ),
)
model.archive_manager._accept_overwrite_job_dir = True
model.cache = None

An execution is then performed. A JSON file is written in the directory my_experiment of the archive. It contains the input data, output data and metadata. Two files identified at model-level as being persistent have also been copied from the scratch to the archive.

model.execute({"x1": atleast_1d(2.0), "x2": atleast_1d(-2.0)})

Out:

    INFO - 19:44:44: Current root directory of scratch directory is C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\scratch\visualize_model_result.
    INFO - 19:44:44: Current root directory of job directory is C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\archive\visualize_model_result.

{'x1': array([2.]), 'x2': array([-2.]), 'x3': array([1., 2., 3.]), 'y1': 0.0, 'y2': array([1, 2, 3, 4, 5]), 'y3': array(['mock_string'], dtype='<U11'), 'y4': array([6.]), 'y5': array([  6.,  36., 216.]), 'error_code': array([0]), 'model': array(['MockModelPersistent'], dtype='<U19'), 'load_case': array(['LC1'], dtype='<U3'), 'description': array([''], dtype='<U1'), 'job_name': array([''], dtype='<U1'), 'persistent_result_files': array(['green_ellipse.png', 'blue_line.png'], dtype='<U17'), 'n_cpus': array([1]), 'date': array(['2026-06-16 19:44:44.317761'], dtype='<U26'), 'cpu_time': array([0.02451634]), 'user': array(['sebastien.bocquet'], dtype='<U17'), 'machine': array(['PCBM20250036'], dtype='<U12'), 'vims_git_version': array(['5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0'], dtype='<U40'), 'directory_archive_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\archive\\visualize_model_result'],
      dtype='<U102'), 'directory_archive_job': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\archive\\visualize_model_result\\MockModelPersistent\\LC1\\4'],
      dtype='<U128'), 'directory_scratch_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result'],
      dtype='<U102'), 'directory_scratch_job': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result\\MockModelPersistent\\LC1\\4'],
      dtype='<U128')}

It often occurs that a cache becomes unusable due to: - a change of VIMSEO version, altering the input, output or metadata of a model, - a mix of successful and failed simulations (by fail, we mean that models may be fault tolerant and outputs NaNs in the output data in case of errors). In general, we want to filter out failed runs. - a large number of results, and for part of them, we have no traceability about and we do not trust the results. Then, a useful feature is to generate a cache file from an archive. create_cache_from_archive() generates a cache file with same name as the current cache suffixed by '_from_archive'. For a DirectoryArchive, the whole archive is considered (no filtering possible):

cache_from_archive = model.create_cache_from_archive()
print(cache_from_archive)

Out:

Name: node
   Type: HDF5Cache
   Tolerance: 1e-10
   Input names: ['x1', 'x2', 'x3']
   Output names: ['cpu_time', 'date', 'description', 'directory_archive_job', 'directory_archive_root', 'directory_scratch_job', 'directory_scratch_root', 'error_code', 'job_name', 'load_case', 'machine', 'model', 'n_cpus', 'persistent_result_files', 'user', 'vims_git_version', 'y1', 'y2', 'y3', 'y4', 'y5']
   Length: 1
   HDF file path: C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\caches\visualize_model_result\MockModelPersistent_LC1_from_archive.hdf
   HDF node path: node

An archive storage based on MLflow is also available. MLflow stores the runs by so-called 'Experiments'. The default experiment name is {model_name}_{load_case}. Again, we create a model instance:

model_name = "BendingTestAnalytical"
load_case = "Cantilever"
model = create_model(
    model_name,
    load_case,
    model_options=IntegratedModelSettings(
        directory_archive_root=EXAMPLE_RUNS_DIR
        / "mlflow_archive/visualize_model_result",
        directory_scratch_root=EXAMPLE_RUNS_DIR / "scratch/visualize_model_result",
        cache_file_path=EXAMPLE_RUNS_DIR
        / f"caches/visualize_model_result/{model_name}_{load_case}_cache.hdf",
        archive_manager="MlflowArchive",
    ),
)
model.cache = None

All runs for the default and specific (specified below as my_experiment) experiment name are first deleted:

runs = mlflow.search_runs(
    experiment_names=["BendingTestAnalytical_Cantilever", "my_experiment"]
)
for id in runs["run_id"]:
    delete_run(id)

# A first execution is done. Its result is stored under the default experiment name,
# which is ``{model_name}_{load_case}``:
model.execute({"height": atleast_1d(40.0)})

Out:

    INFO - 19:44:44: Current root directory of job directory is file:///C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\mlflow_archive\visualize_model_result.

{'length': array([600.]), 'width': array([30.]), 'height': array([40.]), 'imposed_dplt': array([-5.]), 'relative_dplt_location': array([1.]), 'young_modulus': array([210000.]), 'nu_p': array([0.3]), 'reaction_forces': array([-2333.33333333]), 'moment': array([1400000.,       0.]), 'moment_grid': array([  0., 600.]), 'dplt': array([ 0.00000000e+00, -7.62651513e-04, -3.04029995e-03, -6.81748616e-03,
       -1.20787510e-02, -1.88086353e-02, -2.69916799e-02, -3.66124257e-02,
       -4.76554134e-02, -6.01051841e-02, -7.39462784e-02, -8.91632373e-02,
       -1.05740602e-01, -1.23662912e-01, -1.42914710e-01, -1.63480535e-01,
       -1.85344930e-01, -2.08492434e-01, -2.32907588e-01, -2.58574934e-01,
       -2.85479012e-01, -3.13604363e-01, -3.42935528e-01, -3.73457048e-01,
       -4.05153463e-01, -4.38009315e-01, -4.72009144e-01, -5.07137491e-01,
       -5.43378897e-01, -5.80717902e-01, -6.19139049e-01, -6.58626877e-01,
       -6.99165927e-01, -7.40740741e-01, -7.83335858e-01, -8.26935821e-01,
       -8.71525169e-01, -9.17088444e-01, -9.63610186e-01, -1.01107494e+00,
       -1.05946724e+00, -1.10877163e+00, -1.15897265e+00, -1.21005484e+00,
       -1.26200274e+00, -1.31480090e+00, -1.36843385e+00, -1.42288614e+00,
       -1.47814230e+00, -1.53418688e+00, -1.59100442e+00, -1.64857946e+00,
       -1.70689653e+00, -1.76594019e+00, -1.82569497e+00, -1.88614540e+00,
       -1.94727605e+00, -2.00907143e+00, -2.07151610e+00, -2.13459459e+00,
       -2.19829145e+00, -2.26259122e+00, -2.32747844e+00, -2.39293764e+00,
       -2.45895337e+00, -2.52551018e+00, -2.59259259e+00, -2.66018516e+00,
       -2.72827242e+00, -2.79683891e+00, -2.86586918e+00, -2.93534776e+00,
       -3.00525920e+00, -3.07558804e+00, -3.14631882e+00, -3.21743607e+00,
       -3.28892434e+00, -3.36076818e+00, -3.43295211e+00, -3.50546069e+00,
       -3.57827845e+00, -3.65138993e+00, -3.72477968e+00, -3.79843224e+00,
       -3.87233214e+00, -3.94646393e+00, -4.02081214e+00, -4.09536133e+00,
       -4.17009602e+00, -4.24500077e+00, -4.32006011e+00, -4.39525857e+00,
       -4.47058072e+00, -4.54601107e+00, -4.62153419e+00, -4.69713459e+00,
       -4.77279684e+00, -4.84850546e+00, -4.92424500e+00, -5.00000000e+00]), 'dplt_grid': array([  0.        ,   6.06060606,  12.12121212,  18.18181818,
        24.24242424,  30.3030303 ,  36.36363636,  42.42424242,
        48.48484848,  54.54545455,  60.60606061,  66.66666667,
        72.72727273,  78.78787879,  84.84848485,  90.90909091,
        96.96969697, 103.03030303, 109.09090909, 115.15151515,
       121.21212121, 127.27272727, 133.33333333, 139.39393939,
       145.45454545, 151.51515152, 157.57575758, 163.63636364,
       169.6969697 , 175.75757576, 181.81818182, 187.87878788,
       193.93939394, 200.        , 206.06060606, 212.12121212,
       218.18181818, 224.24242424, 230.3030303 , 236.36363636,
       242.42424242, 248.48484848, 254.54545455, 260.60606061,
       266.66666667, 272.72727273, 278.78787879, 284.84848485,
       290.90909091, 296.96969697, 303.03030303, 309.09090909,
       315.15151515, 321.21212121, 327.27272727, 333.33333333,
       339.39393939, 345.45454545, 351.51515152, 357.57575758,
       363.63636364, 369.6969697 , 375.75757576, 381.81818182,
       387.87878788, 393.93939394, 400.        , 406.06060606,
       412.12121212, 418.18181818, 424.24242424, 430.3030303 ,
       436.36363636, 442.42424242, 448.48484848, 454.54545455,
       460.60606061, 466.66666667, 472.72727273, 478.78787879,
       484.84848485, 490.90909091, 496.96969697, 503.03030303,
       509.09090909, 515.15151515, 521.21212121, 527.27272727,
       533.33333333, 539.39393939, 545.45454545, 551.51515152,
       557.57575758, 563.63636364, 569.6969697 , 575.75757576,
       581.81818182, 587.87878788, 593.93939394, 600.        ]), 'dplt_at_force_location': array([-5.]), 'maximum_dplt': array([-5.]), 'location_max_dplt': array([600.]), 'error_code': array([0]), 'model': array(['BendingTestAnalytical'], dtype='<U21'), 'load_case': array(['Cantilever'], dtype='<U10'), 'description': array([''], dtype='<U1'), 'job_name': array([''], dtype='<U1'), 'persistent_result_files': array([''], dtype='<U1'), 'n_cpus': array([1]), 'date': array(['2026-06-16 19:44:45.071343'], dtype='<U26'), 'cpu_time': array([0.04754019]), 'user': array(['sebastien.bocquet'], dtype='<U17'), 'machine': array(['PCBM20250036'], dtype='<U12'), 'vims_git_version': array(['5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0'], dtype='<U40'), 'directory_archive_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result'],
      dtype='<U109'), 'directory_archive_job': array([''], dtype='<U1'), 'directory_scratch_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result'],
      dtype='<U102'), 'directory_scratch_job': array([''], dtype='<U1')}

Then, another experiment is defined. It can be useful to group runs for a given study. The model is then executed for two different beam height:

model.archive_manager.set_experiment("my_experiment")
model.execute({"height": atleast_1d(50.0)})
model.execute({"height": atleast_1d(60.0)})

Out:

    INFO - 19:44:48: Current root directory of job directory is file:///C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\mlflow_archive\visualize_model_result.
    INFO - 19:44:50: Current root directory of job directory is file:///C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\mlflow_archive\visualize_model_result.

{'length': array([600.]), 'width': array([30.]), 'height': array([60.]), 'imposed_dplt': array([-5.]), 'relative_dplt_location': array([1.]), 'young_modulus': array([210000.]), 'nu_p': array([0.3]), 'reaction_forces': array([-7875.]), 'moment': array([4725000.,       0.]), 'moment_grid': array([  0., 600.]), 'dplt': array([ 0.00000000e+00, -7.62651513e-04, -3.04029995e-03, -6.81748616e-03,
       -1.20787510e-02, -1.88086353e-02, -2.69916799e-02, -3.66124257e-02,
       -4.76554134e-02, -6.01051841e-02, -7.39462784e-02, -8.91632373e-02,
       -1.05740602e-01, -1.23662912e-01, -1.42914710e-01, -1.63480535e-01,
       -1.85344930e-01, -2.08492434e-01, -2.32907588e-01, -2.58574934e-01,
       -2.85479012e-01, -3.13604363e-01, -3.42935528e-01, -3.73457048e-01,
       -4.05153463e-01, -4.38009315e-01, -4.72009144e-01, -5.07137491e-01,
       -5.43378897e-01, -5.80717902e-01, -6.19139049e-01, -6.58626877e-01,
       -6.99165927e-01, -7.40740741e-01, -7.83335858e-01, -8.26935821e-01,
       -8.71525169e-01, -9.17088444e-01, -9.63610186e-01, -1.01107494e+00,
       -1.05946724e+00, -1.10877163e+00, -1.15897265e+00, -1.21005484e+00,
       -1.26200274e+00, -1.31480090e+00, -1.36843385e+00, -1.42288614e+00,
       -1.47814230e+00, -1.53418688e+00, -1.59100442e+00, -1.64857946e+00,
       -1.70689653e+00, -1.76594019e+00, -1.82569497e+00, -1.88614540e+00,
       -1.94727605e+00, -2.00907143e+00, -2.07151610e+00, -2.13459459e+00,
       -2.19829145e+00, -2.26259122e+00, -2.32747844e+00, -2.39293764e+00,
       -2.45895337e+00, -2.52551018e+00, -2.59259259e+00, -2.66018516e+00,
       -2.72827242e+00, -2.79683891e+00, -2.86586918e+00, -2.93534776e+00,
       -3.00525920e+00, -3.07558804e+00, -3.14631882e+00, -3.21743607e+00,
       -3.28892434e+00, -3.36076818e+00, -3.43295211e+00, -3.50546069e+00,
       -3.57827845e+00, -3.65138993e+00, -3.72477968e+00, -3.79843224e+00,
       -3.87233214e+00, -3.94646393e+00, -4.02081214e+00, -4.09536133e+00,
       -4.17009602e+00, -4.24500077e+00, -4.32006011e+00, -4.39525857e+00,
       -4.47058072e+00, -4.54601107e+00, -4.62153419e+00, -4.69713459e+00,
       -4.77279684e+00, -4.84850546e+00, -4.92424500e+00, -5.00000000e+00]), 'dplt_grid': array([  0.        ,   6.06060606,  12.12121212,  18.18181818,
        24.24242424,  30.3030303 ,  36.36363636,  42.42424242,
        48.48484848,  54.54545455,  60.60606061,  66.66666667,
        72.72727273,  78.78787879,  84.84848485,  90.90909091,
        96.96969697, 103.03030303, 109.09090909, 115.15151515,
       121.21212121, 127.27272727, 133.33333333, 139.39393939,
       145.45454545, 151.51515152, 157.57575758, 163.63636364,
       169.6969697 , 175.75757576, 181.81818182, 187.87878788,
       193.93939394, 200.        , 206.06060606, 212.12121212,
       218.18181818, 224.24242424, 230.3030303 , 236.36363636,
       242.42424242, 248.48484848, 254.54545455, 260.60606061,
       266.66666667, 272.72727273, 278.78787879, 284.84848485,
       290.90909091, 296.96969697, 303.03030303, 309.09090909,
       315.15151515, 321.21212121, 327.27272727, 333.33333333,
       339.39393939, 345.45454545, 351.51515152, 357.57575758,
       363.63636364, 369.6969697 , 375.75757576, 381.81818182,
       387.87878788, 393.93939394, 400.        , 406.06060606,
       412.12121212, 418.18181818, 424.24242424, 430.3030303 ,
       436.36363636, 442.42424242, 448.48484848, 454.54545455,
       460.60606061, 466.66666667, 472.72727273, 478.78787879,
       484.84848485, 490.90909091, 496.96969697, 503.03030303,
       509.09090909, 515.15151515, 521.21212121, 527.27272727,
       533.33333333, 539.39393939, 545.45454545, 551.51515152,
       557.57575758, 563.63636364, 569.6969697 , 575.75757576,
       581.81818182, 587.87878788, 593.93939394, 600.        ]), 'dplt_at_force_location': array([-5.]), 'maximum_dplt': array([-5.]), 'location_max_dplt': array([600.]), 'error_code': array([0]), 'model': array(['BendingTestAnalytical'], dtype='<U21'), 'load_case': array(['Cantilever'], dtype='<U10'), 'description': array([''], dtype='<U1'), 'job_name': array([''], dtype='<U1'), 'persistent_result_files': array([''], dtype='<U1'), 'n_cpus': array([1]), 'date': array(['2026-06-16 19:44:50.878813'], dtype='<U26'), 'cpu_time': array([0.00713086]), 'user': array(['sebastien.bocquet'], dtype='<U17'), 'machine': array(['PCBM20250036'], dtype='<U12'), 'vims_git_version': array(['5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0'], dtype='<U40'), 'directory_archive_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result'],
      dtype='<U109'), 'directory_archive_job': array([''], dtype='<U1'), 'directory_scratch_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result'],
      dtype='<U102'), 'directory_scratch_job': array([''], dtype='<U1')}

The archive results can be visualized through the MLflow User Interface. The uri of the MLflow database must be specified to the UI: - For exemple, Under Windows: mlflow ui --backend-store-uri file:\\\\\\C:\\Users\\sebastien.bocquet\\PycharmProjects\\vimseo\\tests\\storage_management\\my_experiment - Or under Linux: mlflow ui --backend-store-uri file:////home/sebastien.bocquet/PycharmProjects/vims_only/doc_src/_examples/02-integrated_models/mlflow_archive The uri can be retrieved with model._storage_manager.uri.

The mapping from model raw results (input, output, metadata) and MLflow result tracking framework is done as follows: - all numbers among the input and output data (including arrays of size one, from which the number is extracted), are stored as MLflow metrics. Inputs are distinguished by prefixing their name by inputs., - all other type of data among the input and output data are considered as MLflow params. They are jsonified and stored as strings, - the metadata are stored as MLflow tags. The UI segregates the runs by experiment, and provides a searching functionality. MLflow is compatible with several databases (remote PostgreSql, Amazon S3 etc...), allowing the archive storage to scale (company level or project level).

Runs can also be searched programmatically, here by experiment and for a given input range.

runs = mlflow.search_runs(
    experiment_names=["my_experiment"],
    filter_string="metrics.inputs.height > 50.0",
)
assert len(runs) == 1
runs
run_id experiment_id status artifact_uri start_time end_time metrics.inputs.height metrics.moment_grid metrics.inputs.nu_p metrics.inputs.young_modulus metrics.dplt_grid metrics.dplt_at_force_location metrics.cpu_time metrics.dplt metrics.moment metrics.inputs.relative_dplt_location metrics.location_max_dplt metrics.maximum_dplt metrics.inputs.width metrics.inputs.length metrics.inputs.imposed_dplt metrics.reaction_forces params.dplt params.dplt_grid params.moment_grid params.moment tags.directory_archive_job tags.load_case tags.machine tags.mlflow.source.git.commit tags.n_cpus tags.model tags.error_code tags.vims_git_version tags.job_name tags.mlflow.source.type tags.mlflow.user tags.mlflow.source.name tags.user tags.persistent_result_files tags.directory_scratch_root tags.description tags.directory_archive_root tags.directory_scratch_job tags.mlflow.runName tags.date
0 e08e749fd5574d25b7c299afebdfaf52 550884670975321896 FINISHED file:///C:\Users\sebastien.bocquet\PycharmProj... 2026-06-16 17:44:50.910000+00:00 2026-06-16 17:44:53.048000+00:00 60.0 600.0 0.3 210000.0 600.0 -5.0 0.007131 -5.0 0.0 1.0 600.0 -5.0 30.0 600.0 -5.0 -7875.0 [0.0, -0.0007626515125749897, -0.0030402999487... [0.0, 6.0606060606060606, 12.121212121212121, ... [0.0, 600.0] [4725000.0, 0.0] Cantilever PCBM20250036 5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0 1 BendingTestAnalytical 0 5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0 LOCAL sebastien.bocquet C:/Users/sebastien.bocquet/Dev/vimseo/docs/run... sebastien.bocquet "" C:\Users\sebastien.bocquet\Dev\vimseo\docs\run... C:\Users\sebastien.bocquet\Dev\vimseo\docs\run... burly-sow-330 2026-06-16 19:44:50.878813

Metadata could also be used in the query, for instance tags.user = "a_user". Note that the result returned by get_result() has the following format: {"inputs": input_data, "output": output_data}.

archive_result = model.archive_manager.get_result(runs["run_id"][0])
archive_result

Out:

{'inputs': {'height': array([60.]), 'imposed_dplt': array([-5.]), 'length': array([600.]), 'nu_p': array([0.3]), 'relative_dplt_location': array([1.]), 'width': array([30.]), 'young_modulus': array([210000.])}, 'outputs': {'cpu_time': array([0.00713086]), 'dplt': array([ 0.00000000e+00, -7.62651513e-04, -3.04029995e-03, -6.81748616e-03,
       -1.20787510e-02, -1.88086353e-02, -2.69916799e-02, -3.66124257e-02,
       -4.76554134e-02, -6.01051841e-02, -7.39462784e-02, -8.91632373e-02,
       -1.05740602e-01, -1.23662912e-01, -1.42914710e-01, -1.63480535e-01,
       -1.85344930e-01, -2.08492434e-01, -2.32907588e-01, -2.58574934e-01,
       -2.85479012e-01, -3.13604363e-01, -3.42935528e-01, -3.73457048e-01,
       -4.05153463e-01, -4.38009315e-01, -4.72009144e-01, -5.07137491e-01,
       -5.43378897e-01, -5.80717902e-01, -6.19139049e-01, -6.58626877e-01,
       -6.99165927e-01, -7.40740741e-01, -7.83335858e-01, -8.26935821e-01,
       -8.71525169e-01, -9.17088444e-01, -9.63610186e-01, -1.01107494e+00,
       -1.05946724e+00, -1.10877163e+00, -1.15897265e+00, -1.21005484e+00,
       -1.26200274e+00, -1.31480090e+00, -1.36843385e+00, -1.42288614e+00,
       -1.47814230e+00, -1.53418688e+00, -1.59100442e+00, -1.64857946e+00,
       -1.70689653e+00, -1.76594019e+00, -1.82569497e+00, -1.88614540e+00,
       -1.94727605e+00, -2.00907143e+00, -2.07151610e+00, -2.13459459e+00,
       -2.19829145e+00, -2.26259122e+00, -2.32747844e+00, -2.39293764e+00,
       -2.45895337e+00, -2.52551018e+00, -2.59259259e+00, -2.66018516e+00,
       -2.72827242e+00, -2.79683891e+00, -2.86586918e+00, -2.93534776e+00,
       -3.00525920e+00, -3.07558804e+00, -3.14631882e+00, -3.21743607e+00,
       -3.28892434e+00, -3.36076818e+00, -3.43295211e+00, -3.50546069e+00,
       -3.57827845e+00, -3.65138993e+00, -3.72477968e+00, -3.79843224e+00,
       -3.87233214e+00, -3.94646393e+00, -4.02081214e+00, -4.09536133e+00,
       -4.17009602e+00, -4.24500077e+00, -4.32006011e+00, -4.39525857e+00,
       -4.47058072e+00, -4.54601107e+00, -4.62153419e+00, -4.69713459e+00,
       -4.77279684e+00, -4.84850546e+00, -4.92424500e+00, -5.00000000e+00]), 'dplt_at_force_location': array([-5.]), 'dplt_grid': array([  0.        ,   6.06060606,  12.12121212,  18.18181818,
        24.24242424,  30.3030303 ,  36.36363636,  42.42424242,
        48.48484848,  54.54545455,  60.60606061,  66.66666667,
        72.72727273,  78.78787879,  84.84848485,  90.90909091,
        96.96969697, 103.03030303, 109.09090909, 115.15151515,
       121.21212121, 127.27272727, 133.33333333, 139.39393939,
       145.45454545, 151.51515152, 157.57575758, 163.63636364,
       169.6969697 , 175.75757576, 181.81818182, 187.87878788,
       193.93939394, 200.        , 206.06060606, 212.12121212,
       218.18181818, 224.24242424, 230.3030303 , 236.36363636,
       242.42424242, 248.48484848, 254.54545455, 260.60606061,
       266.66666667, 272.72727273, 278.78787879, 284.84848485,
       290.90909091, 296.96969697, 303.03030303, 309.09090909,
       315.15151515, 321.21212121, 327.27272727, 333.33333333,
       339.39393939, 345.45454545, 351.51515152, 357.57575758,
       363.63636364, 369.6969697 , 375.75757576, 381.81818182,
       387.87878788, 393.93939394, 400.        , 406.06060606,
       412.12121212, 418.18181818, 424.24242424, 430.3030303 ,
       436.36363636, 442.42424242, 448.48484848, 454.54545455,
       460.60606061, 466.66666667, 472.72727273, 478.78787879,
       484.84848485, 490.90909091, 496.96969697, 503.03030303,
       509.09090909, 515.15151515, 521.21212121, 527.27272727,
       533.33333333, 539.39393939, 545.45454545, 551.51515152,
       557.57575758, 563.63636364, 569.6969697 , 575.75757576,
       581.81818182, 587.87878788, 593.93939394, 600.        ]), 'location_max_dplt': array([600.]), 'maximum_dplt': array([-5.]), 'moment': array([4725000.,       0.]), 'moment_grid': array([  0., 600.]), 'reaction_forces': array([-7875.]), 'date': array(['2026-06-16 19:44:50.878813'], dtype='<U26'), 'description': array([''], dtype='<U1'), 'directory_archive_job': array(['\\C:\\Users\\sebastien.bocquet\\PycharmProjects\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result\\550884670975321896\\e08e749fd5574d25b7c299afebdfaf52\\artifacts'],
      dtype='<U184'), 'directory_archive_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result'],
      dtype='<U109'), 'directory_scratch_job': array([''], dtype='<U1'), 'directory_scratch_root': array(['C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result'],
      dtype='<U102'), 'error_code': array([0]), 'job_name': array([''], dtype='<U1'), 'load_case': array(['Cantilever'], dtype='<U10'), 'machine': array(['PCBM20250036'], dtype='<U12'), 'model': array(['BendingTestAnalytical'], dtype='<U21'), 'n_cpus': array([1]), 'user': array(['sebastien.bocquet'], dtype='<U17'), 'vims_git_version': array(['5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0'], dtype='<U40'), <MetaDataNames.persistent_result_files: 'persistent_result_files'>: array([''], dtype='<U1')}}

It can be converted to a user-friendly result format ModelResult that can be easily visualized and compared:

result = ModelResult.from_data(archive_result)
result

Out:

    INFO - 19:44:53: Found 1 entries in the cache file : BendingTestAnalytical_Cantilever_cache.hdf node : node

ModelResult(metadata=ToolResultMetadata(generic={'datetime': '16-06-2026_19-44-53', 'version': '0.1.7.dev4+g13b1eb78b.d20260616'}, misc={}, settings={}, report={<MetaDataNames.model: 'model'>: 'BendingTestAnalytical', <MetaDataNames.load_case: 'load_case'>: 'Cantilever', <MetaDataNames.error_code: 'error_code'>: 0, <MetaDataNames.description: 'description'>: '', <MetaDataNames.job_name: 'job_name'>: '', <MetaDataNames.persistent_result_files: 'persistent_result_files'>: '', <MetaDataNames.n_cpus: 'n_cpus'>: 1, <MetaDataNames.date: 'date'>: '2026-06-16 19:44:50.878813', <MetaDataNames.cpu_time: 'cpu_time'>: 0.007130861282348633, <MetaDataNames.user: 'user'>: 'sebastien.bocquet', <MetaDataNames.machine: 'machine'>: 'PCBM20250036', <MetaDataNames.vims_git_version: 'vims_git_version'>: '5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0', <MetaDataNames.directory_archive_root: 'directory_archive_root'>: 'C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result', <MetaDataNames.directory_archive_job: 'directory_archive_job'>: '\\C:\\Users\\sebastien.bocquet\\PycharmProjects\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result\\550884670975321896\\e08e749fd5574d25b7c299afebdfaf52\\artifacts', <MetaDataNames.directory_scratch_root: 'directory_scratch_root'>: 'C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result', <MetaDataNames.directory_scratch_job: 'directory_scratch_job'>: ''}, model=ModelDescription(name='BendingTestAnalytical', summary=' An analytical model for the bending of a parallelepipedic beam', load_case=Beam_Cantilever(name='Cantilever', domain='Beam', summary='A cantilever load case.', plot_parameters=PlotParameters(curves=[]), bc_variable_names=['imposed_dplt', 'relative_dplt_location'], load=Load(direction='', sign='', type='')), dataflow={'model_inputs': ['length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'model_outputs': ['reaction_forces', 'maximum_dplt', 'dplt_grid', 'location_max_dplt', 'dplt', 'moment', 'moment_grid', 'dplt_at_force_location', 'error_code', 'model', 'load_case', 'description', 'job_name', 'persistent_result_files', 'n_cpus', 'date', 'cpu_time', 'user', 'machine', 'vims_git_version', 'directory_archive_root', 'directory_archive_job', 'directory_scratch_root', 'directory_scratch_job'], 'PreBendingTestAnalytical_Cantilever': {'inputs': ['length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'outputs': ['imposed_dplt_location', 'quadratic_moment', 'reaction_forces', 'moment', 'moment_grid', 'solver', 'boundary']}, 'RunBendingTestAnalytical': {'inputs': ['imposed_dplt_location', 'quadratic_moment', 'reaction_forces', 'moment', 'moment_grid', 'solver', 'boundary', 'length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'outputs': ['dplt', 'dplt_grid', 'moment', 'moment_grid', 'imposed_dplt_location', 'reaction_forces']}, 'PostBendingTestAnalytical_Cantilever': {'inputs': ['dplt', 'dplt_grid', 'moment', 'moment_grid', 'imposed_dplt_location', 'reaction_forces'], 'outputs': ['reaction_forces', 'maximum_dplt', 'dplt_grid', 'location_max_dplt', 'dplt', 'moment', 'moment_grid', 'dplt_at_force_location', 'error_code']}, 'subroutine_names': []}, default_inputs={<InputGroupNames.NUMERICAL_VARS: 'numerical variables'>: {}, <InputGroupNames.BC_VARS: 'boundary conditions variables'>: {'imposed_dplt': [-5.0], 'relative_dplt_location': [1.0]}, <InputGroupNames.GEOMETRICAL_VARS: 'geometrical variables'>: {'length': [600.0], 'width': [30.0], 'height': [40.0]}, <InputGroupNames.MATERIAL_VARS: 'material variables'>: {'young_modulus': [210000.0], 'nu_p': [0.3]}}, curves=[('dplt_grid', 'dplt'), ('moment_grid', 'moment')], verbose=False)), scalars={'height': 60.0, 'imposed_dplt': -5.0, 'length': 600.0, 'nu_p': 0.3, 'relative_dplt_location': 1.0, 'width': 30.0, 'young_modulus': 210000.0, 'dplt_at_force_location': -4.9999999999999964, 'location_max_dplt': 600.0, 'maximum_dplt': -4.9999999999999964, 'reaction_forces': -7875.0}, vectors={}, curves=[<vimseo.utilities.curves.Curve object at 0x0000021A35994650>, <vimseo.utilities.curves.Curve object at 0x0000021A359947D0>], fields={}, snapshots=[])

We perform another query to retrieve the second run:

runs = mlflow.search_runs(
    experiment_names=["my_experiment"],
    filter_string="metrics.inputs.height <= 51.0 AND metrics.inputs.height >= 49.0",
)
assert len(runs) == 1
archive_result_1 = model.archive_manager.get_result(runs["run_id"][0])
result_1 = ModelResult.from_data(archive_result_1)
result_1

Out:

    INFO - 19:44:53: Found 1 entries in the cache file : BendingTestAnalytical_Cantilever_cache.hdf node : node

ModelResult(metadata=ToolResultMetadata(generic={'datetime': '16-06-2026_19-44-53', 'version': '0.1.7.dev4+g13b1eb78b.d20260616'}, misc={}, settings={}, report={<MetaDataNames.model: 'model'>: 'BendingTestAnalytical', <MetaDataNames.load_case: 'load_case'>: 'Cantilever', <MetaDataNames.error_code: 'error_code'>: 0, <MetaDataNames.description: 'description'>: '', <MetaDataNames.job_name: 'job_name'>: '', <MetaDataNames.persistent_result_files: 'persistent_result_files'>: '', <MetaDataNames.n_cpus: 'n_cpus'>: 1, <MetaDataNames.date: 'date'>: '2026-06-16 19:44:48.390038', <MetaDataNames.cpu_time: 'cpu_time'>: 0.004034280776977539, <MetaDataNames.user: 'user'>: 'sebastien.bocquet', <MetaDataNames.machine: 'machine'>: 'PCBM20250036', <MetaDataNames.vims_git_version: 'vims_git_version'>: '5cd5cf5ab4caf1fa2c2190f0396e06b5b5639fd0', <MetaDataNames.directory_archive_root: 'directory_archive_root'>: 'C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result', <MetaDataNames.directory_archive_job: 'directory_archive_job'>: '\\C:\\Users\\sebastien.bocquet\\PycharmProjects\\vimseo\\docs\\runnable_examples\\model_runs\\mlflow_archive\\visualize_model_result\\550884670975321896\\97daff22ce9d4b6992f087578638e3fd\\artifacts', <MetaDataNames.directory_scratch_root: 'directory_scratch_root'>: 'C:\\Users\\sebastien.bocquet\\Dev\\vimseo\\docs\\runnable_examples\\model_runs\\scratch\\visualize_model_result', <MetaDataNames.directory_scratch_job: 'directory_scratch_job'>: ''}, model=ModelDescription(name='BendingTestAnalytical', summary=' An analytical model for the bending of a parallelepipedic beam', load_case=Beam_Cantilever(name='Cantilever', domain='Beam', summary='A cantilever load case.', plot_parameters=PlotParameters(curves=[]), bc_variable_names=['imposed_dplt', 'relative_dplt_location'], load=Load(direction='', sign='', type='')), dataflow={'model_inputs': ['length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'model_outputs': ['reaction_forces', 'maximum_dplt', 'dplt_grid', 'location_max_dplt', 'dplt', 'moment', 'moment_grid', 'dplt_at_force_location', 'error_code', 'model', 'load_case', 'description', 'job_name', 'persistent_result_files', 'n_cpus', 'date', 'cpu_time', 'user', 'machine', 'vims_git_version', 'directory_archive_root', 'directory_archive_job', 'directory_scratch_root', 'directory_scratch_job'], 'PreBendingTestAnalytical_Cantilever': {'inputs': ['length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'outputs': ['imposed_dplt_location', 'quadratic_moment', 'reaction_forces', 'moment', 'moment_grid', 'solver', 'boundary']}, 'RunBendingTestAnalytical': {'inputs': ['imposed_dplt_location', 'quadratic_moment', 'reaction_forces', 'moment', 'moment_grid', 'solver', 'boundary', 'length', 'width', 'height', 'imposed_dplt', 'relative_dplt_location', 'young_modulus', 'nu_p'], 'outputs': ['dplt', 'dplt_grid', 'moment', 'moment_grid', 'imposed_dplt_location', 'reaction_forces']}, 'PostBendingTestAnalytical_Cantilever': {'inputs': ['dplt', 'dplt_grid', 'moment', 'moment_grid', 'imposed_dplt_location', 'reaction_forces'], 'outputs': ['reaction_forces', 'maximum_dplt', 'dplt_grid', 'location_max_dplt', 'dplt', 'moment', 'moment_grid', 'dplt_at_force_location', 'error_code']}, 'subroutine_names': []}, default_inputs={<InputGroupNames.NUMERICAL_VARS: 'numerical variables'>: {}, <InputGroupNames.BC_VARS: 'boundary conditions variables'>: {'imposed_dplt': [-5.0], 'relative_dplt_location': [1.0]}, <InputGroupNames.GEOMETRICAL_VARS: 'geometrical variables'>: {'length': [600.0], 'width': [30.0], 'height': [40.0]}, <InputGroupNames.MATERIAL_VARS: 'material variables'>: {'young_modulus': [210000.0], 'nu_p': [0.3]}}, curves=[('dplt_grid', 'dplt'), ('moment_grid', 'moment')], verbose=False)), scalars={'height': 50.0, 'imposed_dplt': -5.0, 'length': 600.0, 'nu_p': 0.3, 'relative_dplt_location': 1.0, 'width': 30.0, 'young_modulus': 210000.0, 'dplt_at_force_location': -5.000000000000003, 'location_max_dplt': 600.0, 'maximum_dplt': -5.000000000000003, 'reaction_forces': -4557.291666666667}, vectors={}, curves=[<vimseo.utilities.curves.Curve object at 0x0000021A326047D0>, <vimseo.utilities.curves.Curve object at 0x0000021A359BEA50>], fields={}, snapshots=[])

It is possible to create a cache file from the archive. By default, the runs of the current experiment are considered. Since experiment "my_experiment" has two runs, we expect the created cache to have two entries:

cache_from_archive = model.create_cache_from_archive()
print(cache_from_archive)

Out:

Name: node
   Type: HDF5Cache
   Tolerance: 1e-10
   Input names: ['height', 'imposed_dplt', 'length', 'nu_p', 'relative_dplt_location', 'width', 'young_modulus']
   Output names: ['cpu_time', 'date', 'description', 'directory_archive_job', 'directory_archive_root', 'directory_scratch_job', 'directory_scratch_root', 'dplt', 'dplt_at_force_location', 'dplt_grid', 'error_code', 'job_name', 'load_case', 'location_max_dplt', 'machine', 'maximum_dplt', 'model', 'moment', 'moment_grid', 'n_cpus', 'persistent_result_files', 'reaction_forces', 'user', 'vims_git_version']
   Length: 2
   HDF file path: C:\Users\sebastien.bocquet\Dev\vimseo\docs\runnable_examples\model_runs\caches\visualize_model_result\BendingTestAnalytical_Cantilever_from_archive.hdf
   HDF node path: node

The displacement curves of both results can be compared.They are identical for this displacement-imposed loading:

plot_curves(
    [
        result.get_curve(("dplt_grid", "dplt")),
        result_1.get_curve(("dplt_grid", "dplt")),
    ],
    labels=["result", "result 1"],
    save=False,
    show=True,
)

However the resistance force at beam end are different:

variable_names = ["height", "reaction_forces"]
df = concat(
    [
        DataFrame([result.get_numeric_scalars(variable_names=variable_names)]),
        DataFrame([result_1.get_numeric_scalars(variable_names=variable_names)]),
    ],
    ignore_index=True,
)
plot = BarPlot(Dataset.from_dataframe(df))
plot.title = "Comparison of model result with data"
plot.font_size = 20
plot.labels = ["height 50mm", "height 60mm"]
fig = plot.execute(save=False, show=True, file_format="html")[0]
fig

Total running time of the script: ( 0 minutes 10.044 seconds)

Download Python source code: plot_03_model_result_management.py

Download Jupyter notebook: plot_03_model_result_management.ipynb

Gallery generated by mkdocs-gallery