Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
!temoa/
!tests/
!docs/
!data_files/
!notebooks/
!output_files/
!.github/
Expand Down
2 changes: 1 addition & 1 deletion docs/source/computational_implementation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ You can also explore the various development branches in the repository:
.. code::

$ ls
data_files stochastic temoa_model create_archive.sh README.txt
stochastic temoa tests pyproject.toml README.md

$ git branch -a
* energysystem
Expand Down
4 changes: 2 additions & 2 deletions docs/source/quick_start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ Running Temoa
-------------

To run the model, a configuration (``config``) file is needed. An
example config file called :code:`config_sample.toml` resides within the
:code:`data_files/my_configs` folder. Running the model with a config file allows
example config file called :code:`config_sample.toml` is available as a package resource
in :code:`temoa/tutorial_assets`. Running the model with a config file allows
Comment on lines +101 to +102
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Point readers to temoa tutorial, not the internal resource path.

config_sample.toml under temoa/tutorial_assets is not runnable by itself; the tutorial flow also generates the SQLite database and copies mc_settings.csv alongside it. As written, this suggests users can use the packaged file directly.

📝 Suggested doc tweak
-To run the model, a configuration (``config``) file is needed. An
-example config file called :code:`config_sample.toml` is available as a package resource
-in :code:`temoa/tutorial_assets`. Running the model with a config file allows
+To run the model, a configuration (``config``) file is needed. The supported
+way to create the sample config and its companion tutorial assets is:
+
+.. parsed-literal::
+  $ temoa tutorial my_first_model
+
+This copies :code:`config_sample.toml`, generates the SQLite tutorial database,
+and copies :code:`mc_settings.csv` into the working directory. Running the model with a config file allows
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
example config file called :code:`config_sample.toml` is available as a package resource
in :code:`temoa/tutorial_assets`. Running the model with a config file allows
To run the model, a configuration (``config``) file is needed. The supported
way to create the sample config and its companion tutorial assets is:
.. parsed-literal::
$ temoa tutorial my_first_model
This copies :code:`config_sample.toml`, generates the SQLite tutorial database,
and copies :code:`mc_settings.csv` into the working directory. Running the model with a config file allows
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/source/quick_start.rst` around lines 101 - 102, Update the sentence that
references the packaged config_sample.toml to point readers to the temoa
tutorial rather than implying the packaged file in temoa/tutorial_assets is
directly runnable; mention that config_sample.toml alone is insufficient because
the tutorial flow also generates the SQLite database and copies mc_settings.csv
alongside it, and instruct users to follow the temoa tutorial to obtain a fully
runnable setup (reference config_sample.toml and temoa tutorial by name to
locate the correct docs).

the user to (1) use a sqlite database for storing input and output data, (2)
create a formatted Excel output file, (2) specify the solver to use, (3) return
the log file produced during model execution, (4) return the lp file utilized by
Expand Down
14 changes: 13 additions & 1 deletion temoa/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,8 @@ def migrate(
if input_path.is_dir():
if output_path is not None:
rich.print(
'[yellow]Warning: --output is ignored when migrating a directory. Originals are overwritten after backup.[/yellow]'
'[yellow]Warning: --output is ignored when migrating a directory. Originals are '
'overwritten after backup.[/yellow]'
)

migration_script = Path(__file__).parent / 'utilities' / 'master_migration.py'
Expand Down Expand Up @@ -570,6 +571,7 @@ def _copy_tutorial_resources(target_config: Path, target_database: Path) -> None
base = resources.files('temoa') / 'tutorial_assets'
config_resource = base / 'config_sample.toml'
sql_resource = base / 'utopia.sql'
mc_settings_resource = base / 'mc_settings.csv'

# Copy configuration file
with config_resource.open('rb') as source:
Expand All @@ -585,11 +587,18 @@ def _copy_tutorial_resources(target_config: Path, target_database: Path) -> None
with sqlite3.connect(target_database) as conn:
conn.executescript(sql_content)

# Copy Monte Carlo settings
with mc_settings_resource.open('rb') as source:
target_mc = target_config.parent / 'mc_settings.csv'
with open(target_mc, 'wb') as target:
shutil.copyfileobj(source, target)
Comment on lines +590 to +594
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Include mc_settings.csv in the tutorial overwrite checks.

This helper now writes a third file into the target directory, but tutorial() still only checks the config and database for pre-existing conflicts. A user-edited mc_settings.csv will be silently clobbered on reruns. Add it to existing_files before calling this helper.

🛡️ Suggested fix
 def tutorial(
@@
     current_dir = Path.cwd()

     target_config = current_dir / f'{config_name}.toml'
     target_database = current_dir / f'{database_name}.sqlite'
+    target_mc = current_dir / 'mc_settings.csv'

     # Check for existing files and handle conflicts
     existing_files = []
     if target_config.exists():
         existing_files.append(str(target_config))
     if target_database.exists():
         existing_files.append(str(target_database))
+    if target_mc.exists():
+        existing_files.append(str(target_mc))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@temoa/cli.py` around lines 590 - 594, The helper writes a third file named
"mc_settings.csv" into the target directory but the tutorial() preflight only
checks config and DB; to avoid silently overwriting user-edited files, add the
target path for mc_settings.csv into existing_files before calling the helper.
Concretely, compute the path the helper uses (target_config.parent /
'mc_settings.csv' or the same expression used in the helper) and append it to
the existing_files collection used by tutorial(), so the overwrite checks
include "mc_settings.csv".


except (ModuleNotFoundError, FileNotFoundError, AttributeError) as e:
logger.exception('Failed to load tutorial resources from package')
# Fallback to development paths (for development environments)
fallback_config = Path(__file__).parent / 'tutorial_assets' / 'config_sample.toml'
fallback_sql = Path(__file__).parent / 'tutorial_assets' / 'utopia.sql'
fallback_mc = Path(__file__).parent / 'tutorial_assets' / 'mc_settings.csv'

if not fallback_config.exists():
raise FileNotFoundError(
Expand All @@ -614,6 +623,9 @@ def _copy_tutorial_resources(target_config: Path, target_database: Path) -> None
with sqlite3.connect(target_database) as conn:
conn.executescript(fallback_sql.read_text(encoding='utf-8'))

# Copy mc_settings from fallback
shutil.copy2(fallback_mc, target_config.parent / 'mc_settings.csv')


def _update_toml_database_paths(config_path: Path, new_database_name: str) -> None:
"""
Expand Down
8 changes: 5 additions & 3 deletions temoa/extensions/method_of_morris/MM_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ relevant `config.toml` file which can then be run.

### Example: `morris_utopia`

1. Convert the `.sql` file in the `example_dbs` folder back to a database:
1. Convert the `.sql` source file to a database. If you have the `morris_utopia.sql` file:

`data_files/example_dbs % sqlite3 morris_utopia.sqlite < morris_utopia.sql`
```bash
sqlite3 morris_utopia.sqlite < morris_utopia.sql
```

2. Observe the markings (3 groups) in the `MMAnalysis` columns in `cost_variable` and `efficiency`.
3. Observe the `morris` configuration comments in the corresponding `morris_utopia.toml` file in `my_configs`.
3. Observe the `morris` configuration comments in the relevant config file (e.g., `morris_utopia.toml`).
Comment on lines +37 to +44
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Specify where the Morris example assets must live.

morris.py now resolves both morris_utopia.sqlite and morris_utopia.toml from Path(__file__).parent, so generating the database in an arbitrary shell directory still leaves the example broken. This section also assumes the reader already has those files. Please document the required destination directory and how to obtain/create both assets here.

📝 Suggested doc update
-1. Convert the `.sql` source file to a database. If you have the `morris_utopia.sql` file:
+1. Place the Morris example assets next to `morris.py` in `temoa/extensions/method_of_morris/`.
+   If you have the `morris_utopia.sql` source, generate the database in that directory:

    ```bash
+   cd temoa/extensions/method_of_morris
    sqlite3 morris_utopia.sqlite < morris_utopia.sql
    ```
+   Ensure `morris_utopia.toml` is present in the same directory before running the example.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@temoa/extensions/method_of_morris/MM_README.md` around lines 37 - 44, The
README omits that the example assets must live next to the extension code;
update the instructions to tell users to run the sqlite conversion and place
both morris_utopia.sqlite and morris_utopia.toml in the
temoa/extensions/method_of_morris directory because morris.py resolves those
files via Path(__file__).parent; include steps to obtain/create
morris_utopia.sql (or download) and to ensure morris_utopia.toml is present in
the same directory before running the example.

4. Run the config as normal.
5. MM analysis is reported on screen and in 2 csv files for the objective and `co2` in the Outputs folder
6. The DB will contain updated values (tagged by scenario name and "dash run") in `output_objective` and `output_emissions`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

NOTE: The below README is OUTDATED but it describes the history of MM application.
Current users should refer to the MM_README.md file in this directory for how-to and
walk-through on the example provided in data_files!
walk-through on the provided example files!

-----------------------
Method of Morris README
Expand Down
16 changes: 11 additions & 5 deletions temoa/extensions/method_of_morris/morris.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,17 @@ def evaluate(param_names: dict[int, list[Any]], param_values: Any,
perturbation_coefficient = 0.2 # minus plus 10% of the baseline values
param_file = morris_root / 'm_params.txt'

db_resource = resources.files('data_files.untracked_data.morris') / 'morris_utopia.sqlite'
config_resource = resources.files('data_files.untracked_data.morris') / 'morris_utopia.toml'
with resources.as_file(db_resource) as db_file, \
resources.as_file(config_resource) as config_path, \
sqlite3.connect(str(db_file)) as con:
db_file = Path(__file__).parent / 'morris_utopia.sqlite'
config_path = Path(__file__).parent / 'morris_utopia.toml'

if not db_file.exists() or not config_path.exists():
raise FileNotFoundError(
"Example Morris data files not found in current directory. "
"Please see MM_README.md for instructions on how to generate "
"or provide the required 'morris_utopia.sqlite' and 'morris_utopia.toml'."
)

with sqlite3.connect(str(db_file)) as con:
with open(param_file, 'w') as file:
param_names = {}
cur = con.cursor()
Expand Down
14 changes: 12 additions & 2 deletions temoa/extensions/monte_carlo/example_builds/scenario_analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,23 @@
"""
from importlib import resources
from math import sqrt
from pathlib import Path
from sqlite3 import Connection

from matplotlib import pyplot as plt

scenario_name = 'Purple Onion' # must match config file
db_resource = resources.files('data_files.example_dbs') / 'utopia.sqlite'
with resources.as_file(db_resource) as db_path, Connection(str(db_path)) as conn:
# To run this example, ensure tutorial_database.sqlite is in your current directory.
# You can generate it using: temoa tutorial
db_resource = 'tutorial_database.sqlite'

if not Path(db_resource).exists():
raise FileNotFoundError(
f"Database file '{db_resource}' not found. "
"Please run 'temoa tutorial' to create the required files."
)

with Connection(db_resource) as conn:
Comment on lines +12 to +22
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

temoa tutorial alone does not produce analyzable results.

This only guarantees that tutorial_database.sqlite exists. It does not populate output_objective with Purple Onion-* rows, so the query can return nothing and the later histogram call trips on bins=0. Update the setup hint to require running the tutorial model first, and fail fast when no matching rows are found.

📝 Suggested fix
-# To run this example, ensure tutorial_database.sqlite is in your current directory.
-# You can generate it using: temoa tutorial
+# To run this example, first create the tutorial assets and then run the
+# tutorial model so `output_objective` contains `Purple Onion-*` rows:
+#   temoa tutorial
+#   temoa run tutorial_config.toml
 ...
     obj_values = cur.execute(
         f"SELECT total_system_cost FROM output_objective WHERE scenario LIKE '{scenario_name}-%'"
     ).fetchall()
+    if not obj_values:
+        raise RuntimeError(
+            f"No results found for '{scenario_name}'. Run the tutorial model before analyzing it."
+        )
🧰 Tools
🪛 Ruff (0.15.9)

[warning] 17-20: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@temoa/extensions/monte_carlo/example_builds/scenario_analyzer.py` around
lines 12 - 22, The setup check only ensures tutorial_database.sqlite exists
(db_resource) but doesn't verify the DB has analyzable results; open the DB via
Connection(db_resource), run the same query that selects rows from
output_objective (the query that filters for "Purple Onion-*" or equivalent) and
if the result count is zero raise a clear error asking the user to run the
tutorial model (e.g., "Please run 'temoa tutorial model' or the tutorial model
to populate output_objective with 'Purple Onion-*' rows"); also update the
initial help text that references db_resource/Path to instruct running the
tutorial model first. Use identifiers Path, db_resource, and Connection when
locating code to change.

cur = conn.cursor()
obj_values = cur.execute(
f"SELECT total_system_cost FROM output_objective WHERE scenario LIKE '{scenario_name}-%'"
Expand Down
6 changes: 3 additions & 3 deletions temoa/tutorial_assets/config_sample.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ scenario = "zulu"
scenario_mode = "perfect_foresight"

# Input database (Mandatory)
input_database = "data_files/example_dbs/utopia.sqlite"
input_database = "utopia.sqlite"

# Output file (Mandatory)
# The output file must be an existing .sqlite file
# For Perfect Foresight, the user may target the same input file or a separate /
# copied sqlite file in a different location. Myopic, MGA require that input_database = output_database
output_database = "data_files/example_dbs/utopia.sqlite"
output_database = "utopia.sqlite"

# ------------------------------------
# DATABASE CONFIGURATION
Expand Down Expand Up @@ -211,4 +211,4 @@ activity_labels = []

[monte_carlo]
# a path from the PROJECT ROOT to the settings file that contains the run data.
run_settings = 'data_files/monte_carlo/run_settings_1.csv'
run_settings = 'mc_settings.csv'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
3 changes: 3 additions & 0 deletions temoa/tutorial_assets/mc_settings.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
run,param,index,mod,value,notes
1,cost_invest,utopia|TXD|2010,a,-44.0,reduce invest cost to 1000
2,demand,utopia|2010|RH,r,0.1,increase demand by 10%
21 changes: 18 additions & 3 deletions temoa/utilities/db_migration_to_v3.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import sqlite3
import sys
from collections import defaultdict
from importlib import resources
from pathlib import Path

parser = argparse.ArgumentParser()
Expand All @@ -23,14 +24,28 @@
)
parser.add_argument(
'--schema',
help='Path to schema file (default=data_files/temoa_schema_v3.sql)',
help='Path to v3 schema file (defaults to bundled package resource)',
required=False,
dest='schema',
default='data_files/temoa_schema_v3.sql',
default=None,
)
options = parser.parse_args()
legacy_db: Path = Path(options.source_db)
schema_file = Path(options.schema)

# Resolve schema path
if options.schema:
schema_file = Path(options.schema)
else:
try:
# Try package resources first
schema_file = Path(str(resources.files('temoa.db_schema') / 'temoa_schema_v3.sql'))
except (ModuleNotFoundError, FileNotFoundError, AttributeError):
# Fallback to local path relative to this script
schema_file = Path(__file__).parent.parent / 'db_schema' / 'temoa_schema_v3.sql'

if not schema_file.exists():
print(f'Error: Schema file not found: {schema_file}')
sys.exit(1)


new_db_name = legacy_db.stem + '_v3.sqlite'
Expand Down
Loading