"""
Run a simulation
================

OGSTools is a Python library designed to simplify the process of running
simulations with the OpenGeoSys (OGS) framework. The `Project` class is a core
component of OGSTools, providing a convenient interface for creating and
altering OGS6 input files up and executing OGS simulations. This allows you to
automate OGS-workflows in Python via Jupyter or just plain Python scripts.
The development of this functionality was first started in
[ogs6py](https://github.com/joergbuchwald/ogs6py/) and is continued in OGSTools.
Here you'll find the detailed API: :py:obj:`ogstools.ogs6py.project.Project`.

Features:
- alternate existing files (e.g., for parameter sweeps)
- create new input files from scratch
- execute project files
- tailored alteration of input files e.g. for mesh replacements or restarts
- display and export parameter settings

In this guide, we will walk you through the process of using the `Project` class
and then run a simple simulation from an existing model setup.
Assuming you have prepared a model with your mesh and a project file you can
use the following setup, to run it from python.

**Choosing the OGS binary**

Execution details (which OGS binary to use, MPI settings, logging, …) are
controlled via :py:class:`ogstools.core.execution.Execution`.  Pre-built OGS
binaries and container images for each release are available at
`<https://www.opengeosys.org/6.5.7/releases/>`_.

To apply site-wide defaults on a shared system (e.g. HPC cluster), set the
``OGS_EXECUTION_DEFAULTS`` environment variable to a YAML file that overrides
only the keys you need::

    export OGS_EXECUTION_DEFAULTS=/path/to/my_defaults.yaml

See :py:class:`ogstools.core.execution.Execution` for the full list of options.
"""

# %%


import ogstools as ot

model = ot.Model(ot.definitions.EXAMPLES_DIR / "prj" / "simple_mechanics.prj")
sim = model.run()
# Optionally save the simulation data
sim.save()
assert sim.status == ot.Simulation.Status.done
print(sim)


# %% [markdown]
# Manipulating the Project
# ========================
# By using a prj-file as a template and modifying it in python we have an easy
# way to parametrize simulations. Below are some methods, which change
# different parts of the model definition. For more detailed information have
# a look into the API.
# The subsequent code would work but for clarity we recommend saving 2 different states of the prj object into 2 different files.

# Either tell that you are going to change prj object (prj.copy) OR do prj.save() after you have changed but before you run the simulation.

# %%
prj2 = model.project.copy()

# %%
prj2.replace_parameter_value(name="E", value=1e9)
# You can achieve the same via the `replace_text` method:
prj2.replace_text(1e9, xpath="./parameters/parameter[name='E']/value")
# Let's also replace the output prefix of the result
prj2.replace_text("E=1e9", xpath="./time_loop/output/prefix")
# The density of a phase can also be changed
prj2.replace_phase_property_value(
    mediumid=0, phase="Solid", name="density", value="42"
)

# %% [markdown]
# After modifying the Project you can execute the model in the same way as
# before. You have to prj.save(new_name) here, or beforehand by prj2.copy.

# %%
model2 = ot.Model(prj2, meshes=model.meshes)
sim2 = model2.run()
print(sim2)
assert sim2.status == ot.Simulation.Status.done
assert sim2.meshseries != sim.meshseries

# %%
# Alternatively, this call is not blocking:
simc_a = model2.controller()
simc_b = model2.controller()  # As an example: Fire up a second simulation
# simc_a.terminate() aborts a simulation early, if needed
sim_a, sim_b = (
    simc_a.run(),
    simc_b.run(),
)  # Both run concurrently, here we wait for both to finish


# %% [markdown]
# Monitoring a running simulation
# ================================
# ``controller()`` starts the simulation but does not block, so you can watch
# its progress live while it runs. ``plot_log()`` opens the same interactive
# Bokeh dashboard as the ``ogsmonitor`` command line tool, in a real browser tab

# %%
simc = model2.controller()
dashboard = simc.plot_log(
    log_data=[["step_start_time", "step_size"], ["iteration_number", "dx_x_0"]],
    notebook=False,
)

# %% [markdown]
# ``plot_log()`` also accepts a ``notebook`` flag: with ``notebook=True`` the
# plot is embedded directly in the notebook cell's output instead.
#
# .. warning::
#    In VS Code's Jupyter extension, ``notebook=True`` does not live-update:
#    this is an unresolved upstream limitation
#    (`bokeh/jupyter_bokeh#199 <https://github.com/bokeh/jupyter_bokeh/issues/199>`_),
#    not something ogstools can work around.
#
# For that reason, ``notebook=False`` (default) is
# recommended, including in VS Code. A dashboard opens in a real browser tab.
# You need to switch to that tab to see the live charts (see screenshot
# below).
#
# Here we wait for the simulation to finish and then close the dashboard, so
# building these docs doesn't leave a dashboard process running.

# %%
simc.run()
# simc.terminate() stops the simulation *and* closes any dashboards opened
# from it. The returned `dashboard`
# additionally supports its own .terminate() (notebook=False only) to close
# just the browser tab while leaving the simulation running.
simc.terminate()

# %% [markdown]
# .. note::
#    Equivalently, from a separate terminal::
#
#        ogsmonitor /path/to/log.txt
#
#    See :doc:`the monitor user guide </user-guide/monitor>` for more details.
#
# .. figure:: /examples/howto_simulation/bokeh_logs.png
#    :alt: Screenshot of interactive ogs monitor
#
#    Screenshot of the interactive Bokeh dashboard opened by ``plot_log()``
#    or ``ogsmonitor``.

# %% [markdown]
# Creating a Project from scratch
# ===============================
# You can also create a Project without a prj-file. Have a look at this example
# to see how: :ref:`sphx_glr_auto_examples_howto_prjfile_plot_creation.py`
