Skip to content

PMP annual cycle implementation#221

Merged
lewisjared merged 48 commits intomainfrom
pmp-annual-cycle
Apr 30, 2025
Merged

PMP annual cycle implementation#221
lewisjared merged 48 commits intomainfrom
pmp-annual-cycle

Conversation

@lee1043
Copy link
Copy Markdown
Contributor

@lee1043 lee1043 commented Apr 1, 2025

Description

PR generated for tracking changes (not ready until marked as ready)

Checklist

Please confirm that this pull request has done the following:

  • Tests added
  • Documentation added (where applicable)
  • Changelog item added to changelog/

@lee1043 lee1043 self-assigned this Apr 1, 2025
@lee1043 lee1043 linked an issue Apr 1, 2025 that may be closed by this pull request
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/pmp_driver.py Outdated
@lee1043 lee1043 marked this pull request as ready for review April 10, 2025 22:23
@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 10, 2025

@lewisjared could you take this a look when you get a chance? There are some log messages that I don't really understand. I rebased my PR to the latest main, I am not sure if that influenced.

Log message
2025-04-10 15:21:04.161 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'percent' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.161 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining '%' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.162 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'year' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.162 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'yr' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.162 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'C' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.163 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'd' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.163 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'h' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.163 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'degrees_north' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.164 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'degrees_east' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.164 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining 'degrees' (<class 'pint.delegates.txt_defparser.plain.UnitDefinition'>)
2025-04-10 15:21:04.164 | WARNING  | pint.facets.plain.registry:_helper_single_adder:541 - Redefining '[speed]' (<class 'pint.delegates.txt_defparser.plain.DerivedDimensionDefinition'>)
╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1964 in _exec_single_context              │
│                                                                              │
│   1961 │   │   │   │   │   │   │   evt_handled = True                        │
│   1962 │   │   │   │   │   │   │   break                                     │
│   1963 │   │   │   │   if not evt_handled:                                   │
│ ❱ 1964 │   │   │   │   │   self.dialect.do_execute(                          │
│   1965 │   │   │   │   │   │   cursor, str_statement, effective_parameters,  │
│   1966 │   │   │   │   │   )                                                 │
│   1967                                                                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │              context = <sqlalchemy.dialects.sqlite.base.SQLiteExecution… │ │
│ │                        object at 0x11ed487d0>                            │ │
│ │               cursor = <sqlite3.Cursor object at 0x11ecdecc0>            │ │
│ │              dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDiale… │ │
│ │                        object at 0x11719f1d0>                            │ │
│ │ effective_parameters = ('esmvaltool', 'ESMValTool', '0.3.1')             │ │
│ │          evt_handled = False                                             │ │
│ │           parameters = [('esmvaltool', 'ESMValTool', '0.3.1')]           │ │
│ │                 self = <sqlalchemy.engine.base.Connection object at      │ │
│ │                        0x11dcee990>                                      │ │
│ │            statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler   │ │
│ │                        object at 0x11ed48b50>                            │ │
│ │        str_statement = 'INSERT INTO provider (slug, name, version)       │ │
│ │                        VALUES (?, ?, ?) RETURNING id, create'+16         │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/default.py:942 in do_execute                      │
│                                                                              │
│    939 │   │   cursor.executemany(statement, parameters)                     │
│    940 │                                                                     │
│    941 │   def do_execute(self, cursor, statement, parameters, context=None) │
│ ❱  942 │   │   cursor.execute(statement, parameters)                         │
│    943 │                                                                     │
│    944 │   def do_execute_no_params(self, cursor, statement, context=None):  │
│    945 │   │   cursor.execute(statement)                                     │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │    context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext     │ │
│ │              object at 0x11ed487d0>                                      │ │
│ │     cursor = <sqlite3.Cursor object at 0x11ecdecc0>                      │ │
│ │ parameters = ('esmvaltool', 'ESMValTool', '0.3.1')                       │ │
│ │       self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite │ │
│ │              object at 0x11719f1d0>                                      │ │
│ │  statement = 'INSERT INTO provider (slug, name, version) VALUES (?, ?,   │ │
│ │              ?) RETURNING id, create'+16                                 │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────╯
IntegrityError: UNIQUE constraint failed: provider.slug

The above exception was the direct cause of the following exception:

╭───────────────────── Traceback (most recent call last) ──────────────────────╮
│ /Users/lee1043/Documents/Research/git/climate-ref/packages/ref/src/cmip_ref/ │
│ cli/solve.py:23 in solve                                                     │
│                                                                              │
│   20 │   config = ctx.obj.config                                             │
│   21 │   db = ctx.obj.database                                               │
│   22 │   with ctx.obj.database.session.begin():                              │
│ ❱ 23 │   │   solve_metrics(config=config, db=db, dry_run=dry_run, timeout=ti │
│   24                                                                         │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │  config = Config(                                                        │ │
│ │           │   paths=PathConfig(                                          │ │
│ │           │   │   log=PosixPath('/Users/lee1043/Library/Application      │ │
│ │           Support/cmip_ref/log'),                                        │ │
│ │           │   │   scratch=PosixPath('/Users/lee1043/Library/Application  │ │
│ │           Support/cmip_ref/scratch'),                                    │ │
│ │           │   │   software=PosixPath('/Users/lee1043/Library/Application │ │
│ │           Support/cmip_ref/software'),                                   │ │
│ │           │   │   results=PosixPath('/Users/lee1043/Library/Application  │ │
│ │           Support/cmip_ref/results')                                     │ │
│ │           │   ),                                                         │ │
│ │           │   db=DbConfig(                                               │ │
│ │           │   │                                                          │ │
│ │           database_url='sqlite:////Users/lee1043/Library/Application     │ │
│ │           Support/cmip_ref/db/cmip_ref.db',                              │ │
│ │           │   │   run_migrations=True                                    │ │
│ │           │   ),                                                         │ │
│ │           │   executor=ExecutorConfig(                                   │ │
│ │           │   │   executor='cmip_ref.executor.local.LocalExecutor',      │ │
│ │           │   │   config={}                                              │ │
│ │           │   ),                                                         │ │
│ │           │   metric_providers=[                                         │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_esmvaltool.provider',   │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   ),                                                     │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_ilamb.provider',        │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   ),                                                     │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_pmp.provider',          │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   )                                                      │ │
│ │           │   ]                                                          │ │
│ │           )                                                              │ │
│ │     ctx = <click.core.Context object at 0x115da8f10>                     │ │
│ │      db = <cmip_ref.database.Database object at 0x11719ee50>             │ │
│ │ dry_run = False                                                          │ │
│ │ timeout = 60                                                             │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/packages/ref/src/cmip_ref/ │
│ solver.py:235 in solve_metrics                                               │
│                                                                              │
│   232 │   if config is None:                                                 │
│   233 │   │   config = Config.default()                                      │
│   234 │   if solver is None:                                                 │
│ ❱ 235 │   │   solver = MetricSolver.build_from_db(config, db)                │
│   236 │                                                                      │
│   237 │   logger.info("Solving for metrics that require recalculation...")   │
│   238                                                                        │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │  config = Config(                                                        │ │
│ │           │   paths=PathConfig(                                          │ │
│ │           │   │   log=PosixPath('/Users/lee1043/Library/Application      │ │
│ │           Support/cmip_ref/log'),                                        │ │
│ │           │   │   scratch=PosixPath('/Users/lee1043/Library/Application  │ │
│ │           Support/cmip_ref/scratch'),                                    │ │
│ │           │   │   software=PosixPath('/Users/lee1043/Library/Application │ │
│ │           Support/cmip_ref/software'),                                   │ │
│ │           │   │   results=PosixPath('/Users/lee1043/Library/Application  │ │
│ │           Support/cmip_ref/results')                                     │ │
│ │           │   ),                                                         │ │
│ │           │   db=DbConfig(                                               │ │
│ │           │   │                                                          │ │
│ │           database_url='sqlite:////Users/lee1043/Library/Application     │ │
│ │           Support/cmip_ref/db/cmip_ref.db',                              │ │
│ │           │   │   run_migrations=True                                    │ │
│ │           │   ),                                                         │ │
│ │           │   executor=ExecutorConfig(                                   │ │
│ │           │   │   executor='cmip_ref.executor.local.LocalExecutor',      │ │
│ │           │   │   config={}                                              │ │
│ │           │   ),                                                         │ │
│ │           │   metric_providers=[                                         │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_esmvaltool.provider',   │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   ),                                                     │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_ilamb.provider',        │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   ),                                                     │ │
│ │           │   │   MetricsProviderConfig(                                 │ │
│ │           │   │   │   provider='cmip_ref_metrics_pmp.provider',          │ │
│ │           │   │   │   config={}                                          │ │
│ │           │   │   )                                                      │ │
│ │           │   ]                                                          │ │
│ │           )                                                              │ │
│ │      db = <cmip_ref.database.Database object at 0x11719ee50>             │ │
│ │ dry_run = False                                                          │ │
│ │  solver = None                                                           │ │
│ │ timeout = 60                                                             │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/packages/ref/src/cmip_ref/ │
│ solver.py:188 in build_from_db                                               │
│                                                                              │
│   185 │   │   │   A new MetricSolver instance                                │
│   186 │   │   """                                                            │
│   187 │   │   return MetricSolver(                                           │
│ ❱ 188 │   │   │   provider_registry=ProviderRegistry.build_from_config(confi │
│   189 │   │   │   data_catalog={                                             │
│   190 │   │   │   │   SourceDatasetType.CMIP6: CMIP6DatasetAdapter().load_ca │
│   191 │   │   │   │   SourceDatasetType.obs4MIPs: Obs4MIPsDatasetAdapter().l │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ config = Config(                                                         │ │
│ │          │   paths=PathConfig(                                           │ │
│ │          │   │   log=PosixPath('/Users/lee1043/Library/Application       │ │
│ │          Support/cmip_ref/log'),                                         │ │
│ │          │   │   scratch=PosixPath('/Users/lee1043/Library/Application   │ │
│ │          Support/cmip_ref/scratch'),                                     │ │
│ │          │   │   software=PosixPath('/Users/lee1043/Library/Application  │ │
│ │          Support/cmip_ref/software'),                                    │ │
│ │          │   │   results=PosixPath('/Users/lee1043/Library/Application   │ │
│ │          Support/cmip_ref/results')                                      │ │
│ │          │   ),                                                          │ │
│ │          │   db=DbConfig(                                                │ │
│ │          │   │                                                           │ │
│ │          database_url='sqlite:////Users/lee1043/Library/Application      │ │
│ │          Support/cmip_ref/db/cmip_ref.db',                               │ │
│ │          │   │   run_migrations=True                                     │ │
│ │          │   ),                                                          │ │
│ │          │   executor=ExecutorConfig(                                    │ │
│ │          │   │   executor='cmip_ref.executor.local.LocalExecutor',       │ │
│ │          │   │   config={}                                               │ │
│ │          │   ),                                                          │ │
│ │          │   metric_providers=[                                          │ │
│ │          │   │   MetricsProviderConfig(                                  │ │
│ │          │   │   │   provider='cmip_ref_metrics_esmvaltool.provider',    │ │
│ │          │   │   │   config={}                                           │ │
│ │          │   │   ),                                                      │ │
│ │          │   │   MetricsProviderConfig(                                  │ │
│ │          │   │   │   provider='cmip_ref_metrics_ilamb.provider',         │ │
│ │          │   │   │   config={}                                           │ │
│ │          │   │   ),                                                      │ │
│ │          │   │   MetricsProviderConfig(                                  │ │
│ │          │   │   │   provider='cmip_ref_metrics_pmp.provider',           │ │
│ │          │   │   │   config={}                                           │ │
│ │          │   │   )                                                       │ │
│ │          │   ]                                                           │ │
│ │          )                                                               │ │
│ │     db = <cmip_ref.database.Database object at 0x11719ee50>              │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/packages/ref/src/cmip_ref/ │
│ provider_registry.py:94 in build_from_config                                 │
│                                                                              │
│   91 │   │                                                                   │
│   92 │   │   with db.session.begin_nested():                                 │
│   93 │   │   │   for provider in providers:                                  │
│ ❱ 94 │   │   │   │   _register_provider(db, provider)                        │
│   95 │   │                                                                   │
│   96 │   │   return ProviderRegistry(providers=providers)                    │
│   97                                                                         │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │        config = Config(                                                  │ │
│ │                 │   paths=PathConfig(                                    │ │
│ │                 │   │                                                    │ │
│ │                 log=PosixPath('/Users/lee1043/Library/Application        │ │
│ │                 Support/cmip_ref/log'),                                  │ │
│ │                 │   │                                                    │ │
│ │                 scratch=PosixPath('/Users/lee1043/Library/Application    │ │
│ │                 Support/cmip_ref/scratch'),                              │ │
│ │                 │   │                                                    │ │
│ │                 software=PosixPath('/Users/lee1043/Library/Application   │ │
│ │                 Support/cmip_ref/software'),                             │ │
│ │                 │   │                                                    │ │
│ │                 results=PosixPath('/Users/lee1043/Library/Application    │ │
│ │                 Support/cmip_ref/results')                               │ │
│ │                 │   ),                                                   │ │
│ │                 │   db=DbConfig(                                         │ │
│ │                 │   │                                                    │ │
│ │                 database_url='sqlite:////Users/lee1043/Library/Applicat… │ │
│ │                 Support/cmip_ref/db/cmip_ref.db',                        │ │
│ │                 │   │   run_migrations=True                              │ │
│ │                 │   ),                                                   │ │
│ │                 │   executor=ExecutorConfig(                             │ │
│ │                 │   │                                                    │ │
│ │                 executor='cmip_ref.executor.local.LocalExecutor',        │ │
│ │                 │   │   config={}                                        │ │
│ │                 │   ),                                                   │ │
│ │                 │   metric_providers=[                                   │ │
│ │                 │   │   MetricsProviderConfig(                           │ │
│ │                 │   │   │                                                │ │
│ │                 provider='cmip_ref_metrics_esmvaltool.provider',         │ │
│ │                 │   │   │   config={}                                    │ │
│ │                 │   │   ),                                               │ │
│ │                 │   │   MetricsProviderConfig(                           │ │
│ │                 │   │   │   provider='cmip_ref_metrics_ilamb.provider',  │ │
│ │                 │   │   │   config={}                                    │ │
│ │                 │   │   ),                                               │ │
│ │                 │   │   MetricsProviderConfig(                           │ │
│ │                 │   │   │   provider='cmip_ref_metrics_pmp.provider',    │ │
│ │                 │   │   │   config={}                                    │ │
│ │                 │   │   )                                                │ │
│ │                 │   ]                                                    │ │
│ │                 )                                                        │ │
│ │            db = <cmip_ref.database.Database object at 0x11719ee50>       │ │
│ │      provider = <cmip_ref_core.providers.CondaMetricsProvider object at  │ │
│ │                 0x116eedf50>                                             │ │
│ │ provider_info = MetricsProviderConfig(                                   │ │
│ │                 │   provider='cmip_ref_metrics_pmp.provider',            │ │
│ │                 │   config={}                                            │ │
│ │                 )                                                        │ │
│ │     providers = [                                                        │ │
│ │                 │   <cmip_ref_core.providers.CondaMetricsProvider object │ │
│ │                 at 0x116eedf50>,                                         │ │
│ │                 │   <cmip_ref_core.providers.MetricsProvider object at   │ │
│ │                 0x11c4c6c90>,                                            │ │
│ │                 │   <cmip_ref_core.providers.CondaMetricsProvider object │ │
│ │                 at 0x117327910>                                          │ │
│ │                 ]                                                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/packages/ref/src/cmip_ref/ │
│ provider_registry.py:43 in _register_provider                                │
│                                                                              │
│   40 │   )                                                                   │
│   41 │   if created:                                                         │
│   42 │   │   logger.info(f"Created provider {provider.slug}")                │
│ ❱ 43 │   │   db.session.flush()                                              │
│   44 │                                                                       │
│   45 │   for metric in provider.metrics():                                   │
│   46 │   │   metric_model, created = db.get_or_create(                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │        created = True                                                    │ │
│ │             db = <cmip_ref.database.Database object at 0x11719ee50>      │ │
│ │       provider = <cmip_ref_core.providers.CondaMetricsProvider object at │ │
│ │                  0x116eedf50>                                            │ │
│ │ provider_model = <Provider slug=esmvaltool version=0.3.1>                │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/session.py:4353 in flush                             │
│                                                                              │
│   4350 │   │   │   return                                                    │
│   4351 │   │   try:                                                          │
│   4352 │   │   │   self._flushing = True                                     │
│ ❱ 4353 │   │   │   self._flush(objects)                                      │
│   4354 │   │   finally:                                                      │
│   4355 │   │   │   self._flushing = False                                    │
│   4356                                                                       │
│                                                                              │
│ ╭───────────────────────────── locals ─────────────────────────────╮         │
│ │ objects = None                                                   │         │
│ │    self = <sqlalchemy.orm.session.Session object at 0x11718e910> │         │
│ ╰──────────────────────────────────────────────────────────────────╯         │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/session.py:4488 in _flush                            │
│                                                                              │
│   4485 │   │   │   transaction.commit()                                      │
│   4486 │   │                                                                 │
│   4487 │   │   except:                                                       │
│ ❱ 4488 │   │   │   with util.safe_reraise():                                 │
│   4489 │   │   │   │   transaction.rollback(_capture_exception=True)         │
│   4490 │                                                                     │
│   4491 │   def bulk_save_objects(                                            │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │                 _reg = True                                              │ │
│ │              deleted = set()                                             │ │
│ │                dirty = set()                                             │ │
│ │        flush_context = <sqlalchemy.orm.unitofwork.UOWTransaction object  │ │
│ │                        at 0x11eb49350>                                   │ │
│ │            is_orphan = False                                             │ │
│ │ is_persistent_orphan = False                                             │ │
│ │                  new = {                                                 │ │
│ │                        │   <sqlalchemy.orm.state.InstanceState object at │ │
│ │                        0x11ecbe210>                                      │ │
│ │                        }                                                 │ │
│ │              objects = None                                              │ │
│ │               objset = None                                              │ │
│ │                 proc = set()                                             │ │
│ │            processed = {                                                 │ │
│ │                        │   <sqlalchemy.orm.state.InstanceState object at │ │
│ │                        0x11ecbe210>                                      │ │
│ │                        }                                                 │ │
│ │                 self = <sqlalchemy.orm.session.Session object at         │ │
│ │                        0x11718e910>                                      │ │
│ │                state = <sqlalchemy.orm.state.InstanceState object at     │ │
│ │                        0x11ecbe210>                                      │ │
│ │          transaction = <sqlalchemy.orm.session.SessionTransaction object │ │
│ │                        at 0x11eca61c0>                                   │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/util/langhelpers.py:146 in __exit__                      │
│                                                                              │
│    143 │   │   │   exc_type, exc_value, exc_tb = self._exc_info              │
│    144 │   │   │   assert exc_value is not None                              │
│    145 │   │   │   self._exc_info = None  # remove potential circular refere │
│ ❱  146 │   │   │   raise exc_value.with_traceback(exc_tb)                    │
│    147 │   │   else:                                                         │
│    148 │   │   │   self._exc_info = None  # remove potential circular refere │
│    149 │   │   │   assert value is not None                                  │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │    exc_tb = <traceback object at 0x11ed49d00>                            │ │
│ │ exc_value = IntegrityError('(sqlite3.IntegrityError) UNIQUE constraint   │ │
│ │             failed: provider.slug')                                      │ │
│ │      self = <sqlalchemy.util.langhelpers.safe_reraise object at          │ │
│ │             0x11ecace80>                                                 │ │
│ │ traceback = None                                                         │ │
│ │     type_ = None                                                         │ │
│ │     value = None                                                         │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/session.py:4449 in _flush                            │
│                                                                              │
│   4446 │   │   try:                                                          │
│   4447 │   │   │   self._warn_on_events = True                               │
│   4448 │   │   │   try:                                                      │
│ ❱ 4449 │   │   │   │   flush_context.execute()                               │
│   4450 │   │   │   finally:                                                  │
│   4451 │   │   │   │   self._warn_on_events = False                          │
│   4452                                                                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │                 _reg = True                                              │ │
│ │              deleted = set()                                             │ │
│ │                dirty = set()                                             │ │
│ │        flush_context = <sqlalchemy.orm.unitofwork.UOWTransaction object  │ │
│ │                        at 0x11eb49350>                                   │ │
│ │            is_orphan = False                                             │ │
│ │ is_persistent_orphan = False                                             │ │
│ │                  new = {                                                 │ │
│ │                        │   <sqlalchemy.orm.state.InstanceState object at │ │
│ │                        0x11ecbe210>                                      │ │
│ │                        }                                                 │ │
│ │              objects = None                                              │ │
│ │               objset = None                                              │ │
│ │                 proc = set()                                             │ │
│ │            processed = {                                                 │ │
│ │                        │   <sqlalchemy.orm.state.InstanceState object at │ │
│ │                        0x11ecbe210>                                      │ │
│ │                        }                                                 │ │
│ │                 self = <sqlalchemy.orm.session.Session object at         │ │
│ │                        0x11718e910>                                      │ │
│ │                state = <sqlalchemy.orm.state.InstanceState object at     │ │
│ │                        0x11ecbe210>                                      │ │
│ │          transaction = <sqlalchemy.orm.session.SessionTransaction object │ │
│ │                        at 0x11eca61c0>                                   │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/unitofwork.py:466 in execute                         │
│                                                                              │
│   463 │   │   │   │   │   n.execute_aggregate(self, set_)                    │
│   464 │   │   else:                                                          │
│   465 │   │   │   for rec in topological.sort(self.dependencies, postsort_ac │
│ ❱ 466 │   │   │   │   rec.execute(self)                                      │
│   467 │                                                                      │
│   468 │   def finalize_flush_changes(self) -> None:                          │
│   469 │   │   """Mark processed objects as clean / deleted after a successfu │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ postsort_actions = [                                                     │ │
│ │                    │   DeleteAll(Mapper[Provider(provider)]),            │ │
│ │                    │   SaveUpdateAll(Mapper[Provider(provider)])         │ │
│ │                    ]                                                     │ │
│ │              rec = SaveUpdateAll(Mapper[Provider(provider)])             │ │
│ │             self = <sqlalchemy.orm.unitofwork.UOWTransaction object at   │ │
│ │                    0x11eb49350>                                          │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/unitofwork.py:642 in execute                         │
│                                                                              │
│   639 │                                                                      │
│   640 │   @util.preload_module("sqlalchemy.orm.persistence")                 │
│   641 │   def execute(self, uow):                                            │
│ ❱ 642 │   │   util.preloaded.orm_persistence.save_obj(                       │
│   643 │   │   │   self.mapper,                                               │
│   644 │   │   │   uow.states_for_mapper_hierarchy(self.mapper, False, False) │
│   645 │   │   │   uow,                                                       │
│                                                                              │
│ ╭──────────────────────────────── locals ─────────────────────────────────╮  │
│ │ self = SaveUpdateAll(Mapper[Provider(provider)])                        │  │
│ │  uow = <sqlalchemy.orm.unitofwork.UOWTransaction object at 0x11eb49350> │  │
│ ╰─────────────────────────────────────────────────────────────────────────╯  │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/persistence.py:93 in save_obj                        │
│                                                                              │
│     90 │   │   │   update,                                                   │
│     91 │   │   )                                                             │
│     92 │   │                                                                 │
│ ❱   93 │   │   _emit_insert_statements(                                      │
│     94 │   │   │   base_mapper,                                              │
│     95 │   │   │   uowtransaction,                                           │
│     96 │   │   │   mapper,                                                   │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │       base_mapper = <Mapper at 0x10d0e5110; Provider>                    │ │
│ │        connection = <sqlalchemy.engine.base.Connection object at         │ │
│ │                     0x11dcee990>                                         │ │
│ │             dict_ = {                                                    │ │
│ │                     │   '_sa_instance_state':                            │ │
│ │                     <sqlalchemy.orm.state.InstanceState object at        │ │
│ │                     0x11ecbe210>,                                        │ │
│ │                     │   'slug': 'esmvaltool',                            │ │
│ │                     │   'version': '0.3.1',                              │ │
│ │                     │   'name': 'ESMValTool'                             │ │
│ │                     }                                                    │ │
│ │      has_identity = False                                                │ │
│ │            insert = <generator object _collect_insert_commands at        │ │
│ │                     0x11ed4c040>                                         │ │
│ │            mapper = <Mapper at 0x10d0e5110; Provider>                    │ │
│ │        row_switch = None                                                 │ │
│ │            single = False                                                │ │
│ │             state = <sqlalchemy.orm.state.InstanceState object at        │ │
│ │                     0x11ecbe210>                                         │ │
│ │            states = <generator object                                    │ │
│ │                     UOWTransaction.states_for_mapper_hierarchy at        │ │
│ │                     0x11ed34240>                                         │ │
│ │  states_to_insert = [                                                    │ │
│ │                     │   (                                                │ │
│ │                     │   │   <sqlalchemy.orm.state.InstanceState object   │ │
│ │                     at 0x11ecbe210>,                                     │ │
│ │                     │   │   {                                            │ │
│ │                     │   │   │   '_sa_instance_state':                    │ │
│ │                     <sqlalchemy.orm.state.InstanceState object at        │ │
│ │                     0x11ecbe210>,                                        │ │
│ │                     │   │   │   'slug': 'esmvaltool',                    │ │
│ │                     │   │   │   'version': '0.3.1',                      │ │
│ │                     │   │   │   'name': 'ESMValTool'                     │ │
│ │                     │   │   },                                           │ │
│ │                     │   │   <Mapper at 0x10d0e5110; Provider>,           │ │
│ │                     │   │   <sqlalchemy.engine.base.Connection object at │ │
│ │                     0x11dcee990>                                         │ │
│ │                     │   )                                                │ │
│ │                     ]                                                    │ │
│ │  states_to_update = []                                                   │ │
│ │             table = Table('provider', MetaData(), Column('id',           │ │
│ │                     Integer(), table=<provider>, primary_key=True,       │ │
│ │                     nullable=False), Column('slug', String(),            │ │
│ │                     table=<provider>, nullable=False), Column('name',    │ │
│ │                     String(), table=<provider>, nullable=False),         │ │
│ │                     Column('version', String(), table=<provider>,        │ │
│ │                     nullable=False), Column('created_at', DateTime(),    │ │
│ │                     table=<provider>, nullable=False,                    │ │
│ │                     server_default=DefaultClause(<sqlalchemy.sql.functi… │ │
│ │                     at 0x10d00b4d0; now>, for_update=False)),            │ │
│ │                     Column('updated_at', DateTime(), table=<provider>,   │ │
│ │                     nullable=False,                                      │ │
│ │                     onupdate=ColumnElementColumnDefault(<sqlalchemy.sql… │ │
│ │                     at 0x10d00bb90; now>),                               │ │
│ │                     server_default=DefaultClause(<sqlalchemy.sql.functi… │ │
│ │                     at 0x10be4c310; now>, for_update=False)),            │ │
│ │                     schema=None)                                         │ │
│ │    uowtransaction = <sqlalchemy.orm.unitofwork.UOWTransaction object at  │ │
│ │                     0x11eb49350>                                         │ │
│ │            update = <generator object _collect_update_commands at        │ │
│ │                     0x11eac2d40>                                         │ │
│ │ update_version_id = None                                                 │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/orm/persistence.py:1233 in _emit_insert_statements       │
│                                                                              │
│   1230 │   │   │   │   │   │   │   execution_options=execution_options,      │
│   1231 │   │   │   │   │   │   )                                             │
│   1232 │   │   │   │   │   else:                                             │
│ ❱ 1233 │   │   │   │   │   │   result = connection.execute(                  │
│   1234 │   │   │   │   │   │   │   statement,                                │
│   1235 │   │   │   │   │   │   │   params,                                   │
│   1236 │   │   │   │   │   │   │   execution_options=execution_options,      │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │                            _ = {'slug', 'name', 'version'}               │ │
│ │                  base_mapper = <Mapper at 0x10d0e5110; Provider>         │ │
│ │                  bookkeeping = True                                      │ │
│ │                  cached_stmt = <sqlalchemy.sql.dml.Insert object at      │ │
│ │                                0x11e37db50>                              │ │
│ │                   connection = <sqlalchemy.engine.base.Connection object │ │
│ │                                at 0x11dcee990>                           │ │
│ │   deterministic_results_reqd = True                                      │ │
│ │               do_executemany = False                                     │ │
│ │                     exec_opt = {                                         │ │
│ │                                │   'compiled_cache':                     │ │
│ │                                <sqlalchemy.util._collections.LRUCache    │ │
│ │                                object at 0x11eca5cb0>                    │ │
│ │                                }                                         │ │
│ │            execution_options = {                                         │ │
│ │                                │   'compiled_cache':                     │ │
│ │                                <sqlalchemy.util._collections.LRUCache    │ │
│ │                                object at 0x11eca5cb0>                    │ │
│ │                                }                                         │ │
│ │             has_all_defaults = False                                     │ │
│ │                  has_all_pks = False                                     │ │
│ │                     hasvalue = False                                     │ │
│ │                       insert = <generator object                         │ │
│ │                                _collect_insert_commands at 0x11ed4c040>  │ │
│ │                       mapper = <Mapper at 0x10d0e5110; Provider>         │ │
│ │                   mapper_rec = <Mapper at 0x10d0e5110; Provider>         │ │
│ │                       params = {                                         │ │
│ │                                │   'name': 'ESMValTool',                 │ │
│ │                                │   'slug': 'esmvaltool',                 │ │
│ │                                │   'version': '0.3.1'                    │ │
│ │                                }                                         │ │
│ │                      records = [                                         │ │
│ │                                │   (                                     │ │
│ │                                │   │                                     │ │
│ │                                <sqlalchemy.orm.state.InstanceState       │ │
│ │                                object at 0x11ecbe210>,                   │ │
│ │                                │   │   {                                 │ │
│ │                                │   │   │   '_sa_instance_state':         │ │
│ │                                <sqlalchemy.orm.state.InstanceState       │ │
│ │                                object at 0x11ecbe210>,                   │ │
│ │                                │   │   │   'slug': 'esmvaltool',         │ │
│ │                                │   │   │   'version': '0.3.1',           │ │
│ │                                │   │   │   'name': 'ESMValTool'          │ │
│ │                                │   │   },                                │ │
│ │                                │   │   {                                 │ │
│ │                                │   │   │   'name': 'ESMValTool',         │ │
│ │                                │   │   │   'slug': 'esmvaltool',         │ │
│ │                                │   │   │   'version': '0.3.1'            │ │
│ │                                │   │   },                                │ │
│ │                                │   │   <Mapper at 0x10d0e5110;           │ │
│ │                                Provider>,                                │ │
│ │                                │   │                                     │ │
│ │                                <sqlalchemy.engine.base.Connection object │ │
│ │                                at 0x11dcee990>,                          │ │
│ │                                │   │   {},                               │ │
│ │                                │   │   False,                            │ │
│ │                                │   │   False                             │ │
│ │                                │   )                                     │ │
│ │                                ]                                         │ │
│ │                return_result = None                                      │ │
│ │ returning_is_required_anyway = False                                     │ │
│ │                        state = <sqlalchemy.orm.state.InstanceState       │ │
│ │                                object at 0x11ecbe210>                    │ │
│ │                   state_dict = {                                         │ │
│ │                                │   '_sa_instance_state':                 │ │
│ │                                <sqlalchemy.orm.state.InstanceState       │ │
│ │                                object at 0x11ecbe210>,                   │ │
│ │                                │   'slug': 'esmvaltool',                 │ │
│ │                                │   'version': '0.3.1',                   │ │
│ │                                │   'name': 'ESMValTool'                  │ │
│ │                                }                                         │ │
│ │                    statement = <sqlalchemy.sql.dml.Insert object at      │ │
│ │                                0x11ed48690>                              │ │
│ │                        table = Table('provider', MetaData(),             │ │
│ │                                Column('id', Integer(), table=<provider>, │ │
│ │                                primary_key=True, nullable=False),        │ │
│ │                                Column('slug', String(),                  │ │
│ │                                table=<provider>, nullable=False),        │ │
│ │                                Column('name', String(),                  │ │
│ │                                table=<provider>, nullable=False),        │ │
│ │                                Column('version', String(),               │ │
│ │                                table=<provider>, nullable=False),        │ │
│ │                                Column('created_at', DateTime(),          │ │
│ │                                table=<provider>, nullable=False,         │ │
│ │                                server_default=DefaultClause(<sqlalchemy… │ │
│ │                                at 0x10d00b4d0; now>, for_update=False)), │ │
│ │                                Column('updated_at', DateTime(),          │ │
│ │                                table=<provider>, nullable=False,         │ │
│ │                                onupdate=ColumnElementColumnDefault(<sql… │ │
│ │                                at 0x10d00bb90; now>),                    │ │
│ │                                server_default=DefaultClause(<sqlalchemy… │ │
│ │                                at 0x10be4c310; now>, for_update=False)), │ │
│ │                                schema=None)                              │ │
│ │               uowtransaction = <sqlalchemy.orm.unitofwork.UOWTransaction │ │
│ │                                object at 0x11eb49350>                    │ │
│ │          use_orm_insert_stmt = None                                      │ │
│ │                 value_params = {}                                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1416 in execute                           │
│                                                                              │
│   1413 │   │   except AttributeError as err:                                 │
│   1414 │   │   │   raise exc.ObjectNotExecutableError(statement) from err    │
│   1415 │   │   else:                                                         │
│ ❱ 1416 │   │   │   return meth(                                              │
│   1417 │   │   │   │   self,                                                 │
│   1418 │   │   │   │   distilled_parameters,                                 │
│   1419 │   │   │   │   execution_options or NO_OPTIONS,                      │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │ distilled_parameters = [                                                 │ │
│ │                        │   {                                             │ │
│ │                        │   │   'name': 'ESMValTool',                     │ │
│ │                        │   │   'slug': 'esmvaltool',                     │ │
│ │                        │   │   'version': '0.3.1'                        │ │
│ │                        │   }                                             │ │
│ │                        ]                                                 │ │
│ │    execution_options = {                                                 │ │
│ │                        │   'compiled_cache':                             │ │
│ │                        <sqlalchemy.util._collections.LRUCache object at  │ │
│ │                        0x11eca5cb0>                                      │ │
│ │                        }                                                 │ │
│ │                 meth = <bound method                                     │ │
│ │                        ClauseElement._execute_on_connection of           │ │
│ │                        <sqlalchemy.sql.dml.Insert object at              │ │
│ │                        0x11ed48690>>                                     │ │
│ │           parameters = {                                                 │ │
│ │                        │   'name': 'ESMValTool',                         │ │
│ │                        │   'slug': 'esmvaltool',                         │ │
│ │                        │   'version': '0.3.1'                            │ │
│ │                        }                                                 │ │
│ │                 self = <sqlalchemy.engine.base.Connection object at      │ │
│ │                        0x11dcee990>                                      │ │
│ │            statement = <sqlalchemy.sql.dml.Insert object at 0x11ed48690> │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/sql/elements.py:516 in _execute_on_connection            │
│                                                                              │
│    513 │   │   if self.supports_execution:                                   │
│    514 │   │   │   if TYPE_CHECKING:                                         │
│    515 │   │   │   │   assert isinstance(self, Executable)                   │
│ ❱  516 │   │   │   return connection._execute_clauseelement(                 │
│    517 │   │   │   │   self, distilled_params, execution_options             │
│    518 │   │   │   )                                                         │
│    519 │   │   else:                                                         │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │        connection = <sqlalchemy.engine.base.Connection object at         │ │
│ │                     0x11dcee990>                                         │ │
│ │  distilled_params = [                                                    │ │
│ │                     │   {                                                │ │
│ │                     │   │   'name': 'ESMValTool',                        │ │
│ │                     │   │   'slug': 'esmvaltool',                        │ │
│ │                     │   │   'version': '0.3.1'                           │ │
│ │                     │   }                                                │ │
│ │                     ]                                                    │ │
│ │ execution_options = {                                                    │ │
│ │                     │   'compiled_cache':                                │ │
│ │                     <sqlalchemy.util._collections.LRUCache object at     │ │
│ │                     0x11eca5cb0>                                         │ │
│ │                     }                                                    │ │
│ │              self = <sqlalchemy.sql.dml.Insert object at 0x11ed48690>    │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1638 in _execute_clauseelement            │
│                                                                              │
│   1635 │   │   │   schema_translate_map=schema_translate_map,                │
│   1636 │   │   │   linting=self.dialect.compiler_linting | compiler.WARN_LIN │
│   1637 │   │   )                                                             │
│ ❱ 1638 │   │   ret = self._execute_context(                                  │
│   1639 │   │   │   dialect,                                                  │
│   1640 │   │   │   dialect.execution_ctx_cls._init_compiled,                 │
│   1641 │   │   │   compiled_sql,                                             │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │            cache_hit = <CacheStats.CACHE_MISS: 1>                        │ │
│ │       compiled_cache = <sqlalchemy.util._collections.LRUCache object at  │ │
│ │                        0x11eca5cb0>                                      │ │
│ │         compiled_sql = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler   │ │
│ │                        object at 0x11ed48b50>                            │ │
│ │              dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDiale… │ │
│ │                        object at 0x11719f1d0>                            │ │
│ │ distilled_parameters = [                                                 │ │
│ │                        │   {                                             │ │
│ │                        │   │   'name': 'ESMValTool',                     │ │
│ │                        │   │   'slug': 'esmvaltool',                     │ │
│ │                        │   │   'version': '0.3.1'                        │ │
│ │                        │   }                                             │ │
│ │                        ]                                                 │ │
│ │                 elem = <sqlalchemy.sql.dml.Insert object at 0x11ed48690> │ │
│ │    execution_options = immutabledict({'compiled_cache':                  │ │
│ │                        <sqlalchemy.util._collections.LRUCache object at  │ │
│ │                        0x11eca5cb0>})                                    │ │
│ │     extracted_params = []                                                │ │
│ │      for_executemany = False                                             │ │
│ │           has_events = False                                             │ │
│ │                 keys = ['name', 'slug', 'version']                       │ │
│ │ schema_translate_map = None                                              │ │
│ │                 self = <sqlalchemy.engine.base.Connection object at      │ │
│ │                        0x11dcee990>                                      │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1843 in _execute_context                  │
│                                                                              │
│   1840 │   │   if context.execute_style is ExecuteStyle.INSERTMANYVALUES:    │
│   1841 │   │   │   return self._exec_insertmany_context(dialect, context)    │
│   1842 │   │   else:                                                         │
│ ❱ 1843 │   │   │   return self._exec_single_context(                         │
│   1844 │   │   │   │   dialect, context, statement, parameters               │
│   1845 │   │   │   )                                                         │
│   1846                                                                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │              args = (                                                    │ │
│ │                     │   <sqlalchemy.dialects.sqlite.base.SQLiteCompiler  │ │
│ │                     object at 0x11ed48b50>,                              │ │
│ │                     │   [                                                │ │
│ │                     │   │   {                                            │ │
│ │                     │   │   │   'name': 'ESMValTool',                    │ │
│ │                     │   │   │   'slug': 'esmvaltool',                    │ │
│ │                     │   │   │   'version': '0.3.1'                       │ │
│ │                     │   │   }                                            │ │
│ │                     │   ],                                               │ │
│ │                     │   <sqlalchemy.sql.dml.Insert object at             │ │
│ │                     0x11ed48690>,                                        │ │
│ │                     │   []                                               │ │
│ │                     )                                                    │ │
│ │              conn = <sqlalchemy.pool.base._ConnectionFairy object at     │ │
│ │                     0x11ecbc230>                                         │ │
│ │       constructor = <bound method DefaultExecutionContext._init_compiled │ │
│ │                     of <class                                            │ │
│ │                     'sqlalchemy.dialects.sqlite.base.SQLiteExecutionCon… │ │
│ │           context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionCon… │ │
│ │                     object at 0x11ed487d0>                               │ │
│ │           dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_… │ │
│ │                     object at 0x11719f1d0>                               │ │
│ │ execution_options = immutabledict({'compiled_cache':                     │ │
│ │                     <sqlalchemy.util._collections.LRUCache object at     │ │
│ │                     0x11eca5cb0>})                                       │ │
│ │                kw = {'cache_hit': <CacheStats.CACHE_MISS: 1>}            │ │
│ │        parameters = [                                                    │ │
│ │                     │   {                                                │ │
│ │                     │   │   'name': 'ESMValTool',                        │ │
│ │                     │   │   'slug': 'esmvaltool',                        │ │
│ │                     │   │   'version': '0.3.1'                           │ │
│ │                     │   }                                                │ │
│ │                     ]                                                    │ │
│ │              self = <sqlalchemy.engine.base.Connection object at         │ │
│ │                     0x11dcee990>                                         │ │
│ │         statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler      │ │
│ │                     object at 0x11ed48b50>                               │ │
│ │                yp = None                                                 │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1983 in _exec_single_context              │
│                                                                              │
│   1980 │   │   │   result = context._setup_result_proxy()                    │
│   1981 │   │                                                                 │
│   1982 │   │   except BaseException as e:                                    │
│ ❱ 1983 │   │   │   self._handle_dbapi_exception(                             │
│   1984 │   │   │   │   e, str_statement, effective_parameters, cursor, conte │
│   1985 │   │   │   )                                                         │
│   1986                                                                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │              context = <sqlalchemy.dialects.sqlite.base.SQLiteExecution… │ │
│ │                        object at 0x11ed487d0>                            │ │
│ │               cursor = <sqlite3.Cursor object at 0x11ecdecc0>            │ │
│ │              dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDiale… │ │
│ │                        object at 0x11719f1d0>                            │ │
│ │ effective_parameters = ('esmvaltool', 'ESMValTool', '0.3.1')             │ │
│ │          evt_handled = False                                             │ │
│ │           parameters = [('esmvaltool', 'ESMValTool', '0.3.1')]           │ │
│ │                 self = <sqlalchemy.engine.base.Connection object at      │ │
│ │                        0x11dcee990>                                      │ │
│ │            statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler   │ │
│ │                        object at 0x11ed48b50>                            │ │
│ │        str_statement = 'INSERT INTO provider (slug, name, version)       │ │
│ │                        VALUES (?, ?, ?) RETURNING id, create'+16         │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:2352 in _handle_dbapi_exception           │
│                                                                              │
│   2349 │   │   │   │   raise newraise.with_traceback(exc_info[2]) from e     │
│   2350 │   │   │   elif should_wrap:                                         │
│   2351 │   │   │   │   assert sqlalchemy_exception is not None               │
│ ❱ 2352 │   │   │   │   raise sqlalchemy_exception.with_traceback(exc_info[2] │
│   2353 │   │   │   else:                                                     │
│   2354 │   │   │   │   assert exc_info[1] is not None                        │
│   2355 │   │   │   │   raise exc_info[1].with_traceback(exc_info[2])         │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │                       context = <sqlalchemy.dialects.sqlite.base.SQLite… │ │
│ │                                 object at 0x11ed487d0>                   │ │
│ │                        cursor = <sqlite3.Cursor object at 0x11ecdecc0>   │ │
│ │                             e = IntegrityError('UNIQUE constraint        │ │
│ │                                 failed: provider.slug')                  │ │
│ │                      exc_info = (                                        │ │
│ │                                 │   <class 'sqlite3.IntegrityError'>,    │ │
│ │                                 │   IntegrityError('UNIQUE constraint    │ │
│ │                                 failed: provider.slug'),                 │ │
│ │                                 │   <traceback object at 0x11ed48d80>    │ │
│ │                                 )                                        │ │
│ │ invalidate_pool_on_disconnect = True                                     │ │
│ │             is_exit_exception = False                                    │ │
│ │                   is_sub_exec = False                                    │ │
│ │                       ismulti = False                                    │ │
│ │                      newraise = None                                     │ │
│ │                    parameters = ('esmvaltool', 'ESMValTool', '0.3.1')    │ │
│ │                          self = <sqlalchemy.engine.base.Connection       │ │
│ │                                 object at 0x11dcee990>                   │ │
│ │                   should_wrap = True                                     │ │
│ │          sqlalchemy_exception = IntegrityError('(sqlite3.IntegrityError) │ │
│ │                                 UNIQUE constraint failed:                │ │
│ │                                 provider.slug')                          │ │
│ │                     statement = 'INSERT INTO provider (slug, name,       │ │
│ │                                 version) VALUES (?, ?, ?) RETURNING id,  │ │
│ │                                 create'+16                               │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/base.py:1964 in _exec_single_context              │
│                                                                              │
│   1961 │   │   │   │   │   │   │   evt_handled = True                        │
│   1962 │   │   │   │   │   │   │   break                                     │
│   1963 │   │   │   │   if not evt_handled:                                   │
│ ❱ 1964 │   │   │   │   │   self.dialect.do_execute(                          │
│   1965 │   │   │   │   │   │   cursor, str_statement, effective_parameters,  │
│   1966 │   │   │   │   │   )                                                 │
│   1967                                                                       │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │              context = <sqlalchemy.dialects.sqlite.base.SQLiteExecution… │ │
│ │                        object at 0x11ed487d0>                            │ │
│ │               cursor = <sqlite3.Cursor object at 0x11ecdecc0>            │ │
│ │              dialect = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDiale… │ │
│ │                        object at 0x11719f1d0>                            │ │
│ │ effective_parameters = ('esmvaltool', 'ESMValTool', '0.3.1')             │ │
│ │          evt_handled = False                                             │ │
│ │           parameters = [('esmvaltool', 'ESMValTool', '0.3.1')]           │ │
│ │                 self = <sqlalchemy.engine.base.Connection object at      │ │
│ │                        0x11dcee990>                                      │ │
│ │            statement = <sqlalchemy.dialects.sqlite.base.SQLiteCompiler   │ │
│ │                        object at 0x11ed48b50>                            │ │
│ │        str_statement = 'INSERT INTO provider (slug, name, version)       │ │
│ │                        VALUES (?, ?, ?) RETURNING id, create'+16         │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
│                                                                              │
│ /Users/lee1043/Documents/Research/git/climate-ref/.venv/lib/python3.11/site- │
│ packages/sqlalchemy/engine/default.py:942 in do_execute                      │
│                                                                              │
│    939 │   │   cursor.executemany(statement, parameters)                     │
│    940 │                                                                     │
│    941 │   def do_execute(self, cursor, statement, parameters, context=None) │
│ ❱  942 │   │   cursor.execute(statement, parameters)                         │
│    943 │                                                                     │
│    944 │   def do_execute_no_params(self, cursor, statement, context=None):  │
│    945 │   │   cursor.execute(statement)                                     │
│                                                                              │
│ ╭───────────────────────────────── locals ─────────────────────────────────╮ │
│ │    context = <sqlalchemy.dialects.sqlite.base.SQLiteExecutionContext     │ │
│ │              object at 0x11ed487d0>                                      │ │
│ │     cursor = <sqlite3.Cursor object at 0x11ecdecc0>                      │ │
│ │ parameters = ('esmvaltool', 'ESMValTool', '0.3.1')                       │ │
│ │       self = <sqlalchemy.dialects.sqlite.pysqlite.SQLiteDialect_pysqlite │ │
│ │              object at 0x11719f1d0>                                      │ │
│ │  statement = 'INSERT INTO provider (slug, name, version) VALUES (?, ?,   │ │
│ │              ?) RETURNING id, create'+16                                 │ │
│ ╰──────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────╯
IntegrityError: (sqlite3.IntegrityError) UNIQUE constraint failed: provider.slug
[SQL: INSERT INTO provider (slug, name, version) VALUES (?, ?, ?) RETURNING id, 
created_at, updated_at]
[parameters: ('esmvaltool', 'ESMValTool', '0.3.1')]
(Background on this error at: https://sqlalche.me/e/20/gkpj)

@lewisjared
Copy link
Copy Markdown
Contributor

What command did you run to get that message?

@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 10, 2025

What command did you run to get that message?

That was uv run ref solve

Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/variability_modes.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/variability_modes.py Outdated
@lee1043 lee1043 added the help wanted Extra attention is needed label Apr 15, 2025
@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 29, 2025

Github action pre-commit test fails with ruff, and my local test results are like below, which I am not clearly understanding.

> ruff check .

packages/ref/src/cmip_ref/config.py:303:25: RUF009 Do not perform function call `Factory` in dataclass defaults
    |
301 |     This value is overridden if a value is specified via the CLI.
302 |     """
303 |     paths: PathConfig = Factory(PathConfig)
    |                         ^^^^^^^^^^^^^^^^^^^ RUF009
304 |     db: DbConfig = Factory(DbConfig)
305 |     executor: ExecutorConfig = Factory(ExecutorConfig)
    |

packages/ref/src/cmip_ref/config.py:304:20: RUF009 Do not perform function call `Factory` in dataclass defaults
    |
302 |     """
303 |     paths: PathConfig = Factory(PathConfig)
304 |     db: DbConfig = Factory(DbConfig)
    |                    ^^^^^^^^^^^^^^^^^ RUF009
305 |     executor: ExecutorConfig = Factory(ExecutorConfig)
306 |     metric_providers: list[MetricsProviderConfig] = Factory(default_metric_providers)
    |

packages/ref/src/cmip_ref/config.py:305:32: RUF009 Do not perform function call `Factory` in dataclass defaults
    |
303 |     paths: PathConfig = Factory(PathConfig)
304 |     db: DbConfig = Factory(DbConfig)
305 |     executor: ExecutorConfig = Factory(ExecutorConfig)
    |                                ^^^^^^^^^^^^^^^^^^^^^^^ RUF009
306 |     metric_providers: list[MetricsProviderConfig] = Factory(default_metric_providers)
307 |     _raw: TOMLDocument | None = field(init=False, default=None, repr=False)
    |

packages/ref/src/cmip_ref/config.py:306:53: RUF009 Do not perform function call `Factory` in dataclass defaults
    |
304 |     db: DbConfig = Factory(DbConfig)
305 |     executor: ExecutorConfig = Factory(ExecutorConfig)
306 |     metric_providers: list[MetricsProviderConfig] = Factory(default_metric_providers)
    |                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF009
307 |     _raw: TOMLDocument | None = field(init=False, default=None, repr=False)
308 |     _config_file: Path | None = field(init=False, default=None, repr=False)
    |

Found 4 errors.

@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 29, 2025

@lewisjared I think this PR is ready. I was able to run it locally and produce expected output files. Can you give a review on this?

@lewisjared
Copy link
Copy Markdown
Contributor

I added a commit to include a test for this new metric. It doesn't currently pass the bundle validation due to #258, but at least that validates that the metric runs to completion.

Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
Comment thread packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py Outdated
@lewisjared
Copy link
Copy Markdown
Contributor

@lee1043 I added some additional tests to increase the test coverage to an acceptable level

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 29, 2025

Codecov Report

❌ Patch coverage is 81.56028% with 26 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...trics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py 82.05% 13 Missing and 1 partial ⚠️
...metrics-pmp/src/cmip_ref_metrics_pmp/pmp_driver.py 81.25% 3 Missing and 3 partials ⚠️
...-pmp/src/cmip_ref_metrics_pmp/variability_modes.py 33.33% 3 Missing and 3 partials ⚠️
Files with missing lines Coverage Δ
...f-metrics-pmp/src/cmip_ref_metrics_pmp/__init__.py 100.00% <100.00%> (ø)
...etrics_pmp/params/pmp_param_annualcycle_1-clims.py 100.00% <100.00%> (ø)
...rics_pmp/params/pmp_param_annualcycle_2-metrics.py 100.00% <100.00%> (ø)
...metrics-pmp/src/cmip_ref_metrics_pmp/pmp_driver.py 86.04% <81.25%> (-1.46%) ⬇️
...-pmp/src/cmip_ref_metrics_pmp/variability_modes.py 86.27% <33.33%> (-11.40%) ⬇️
...trics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py 82.05% <82.05%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 29, 2025

Thank you, @lewisjared for the additions, which are very helpful!

The metric is now working as expected even for the multiple input netcdf files. Also added some unit tests. I am getting the following error but I think that is expected at the moment, as pointed by @minxu74 (please correct me if I am wrong).

---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[25], line 2
      1 definition.output_directory.mkdir(parents=True, exist_ok=True)
----> 2 direct_result = metric.run(definition=definition)
      3 assert direct_result.successful
      5 prettyprinter.pprint(direct_result)

File [/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py:235](https://jupyter.nersc.gov/user/lee1043/perlmutter-login-node-base/lab/tree/pscratch/sd/l/lee1043/PMP/REF_test/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py#line=234), in AnnualCycle.run(self, definition)
    232 runs = [self.provider.run(cmd) for cmd in cmds]
    233 logger.debug(f"runs: {runs}")
--> 235 return self.build_metric_result(definition)

File [/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py:207](https://jupyter.nersc.gov/user/lee1043/perlmutter-login-node-base/lab/tree/pscratch/sd/l/lee1043/PMP/REF_test/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/annual_cycle.py#line=206), in AnnualCycle.build_metric_result(self, definition)
    204 png_files = list(png_directory.glob("*.png"))
    205 data_files = list(data_directory.glob("*.nc"))
--> 207 cmec_output, cmec_metric = process_json_result(results_files[0], png_files, data_files)
    209 return MetricExecutionResult.build_from_output_bundle(
    210     definition,
    211     cmec_output_bundle=cmec_output,
    212     cmec_metric_bundle=cmec_metric,
    213 )

File [/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/pmp_driver.py:103](https://jupyter.nersc.gov/user/lee1043/perlmutter-login-node-base/lab/tree/pscratch/sd/l/lee1043/PMP/REF_test/pscratch/sd/l/lee1043/git/climate-ref/packages/ref-metrics-pmp/src/cmip_ref_metrics_pmp/pmp_driver.py#line=102), in process_json_result(json_filename, png_files, data_files)
    100 logger.info(f"cmec_output: {pretty_repr(cmec_output)}")
    101 logger.info(f"cmec_metric: {pretty_repr(cmec_metric)}")
--> 103 return CMECOutput(**cmec_output), CMECMetric(**cmec_metric)

File [/pscratch/sd/l/lee1043/git/climate-ref/.venv/lib/python3.11/site-packages/pydantic/main.py:214](https://jupyter.nersc.gov/user/lee1043/perlmutter-login-node-base/lab/tree/pscratch/sd/l/lee1043/PMP/REF_test/pscratch/sd/l/lee1043/git/climate-ref/.venv/lib/python3.11/site-packages/pydantic/main.py#line=213), in BaseModel.__init__(self, **data)
    212 # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
    213 __tracebackhide__ = True
--> 214 validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
    215 if self is not validated_self:
    216     warnings.warn(
    217         'A custom validator is returning a value other than `self`.\n'
    218         "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
    219         'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
    220         stacklevel=2,
    221     )

ValidationError: 1 validation error for CMECMetric
  Value error, Unknown dimension values: {'mae_xy', 'std_xy_devzm', 'rms_xy', 'std-obs_xyt', 'mean-obs_xy', 'bias_xy', 'mean_xy', 'rms_devzm', 'rms_y', 'std_xy', 'std_xyt', 'rms_xyt', 'std-obs_xy', 'cor_xy', 'std-obs_xy_devzm', 'rmsc_xy'} [type=value_error, input_value={'ACCESS-CM2': {'default'...': {'units': 'mm/day'}}}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.10/v/value_error

@lee1043
Copy link
Copy Markdown
Contributor Author

lee1043 commented Apr 29, 2025

@lewisjared All CI tests except the codecov/patch are passed. Should I merge the PR or do you prefer to add more tests to increase the test coverage percentage?

@lewisjared
Copy link
Copy Markdown
Contributor

I am getting the following error but I think that is expected at the moment,

Yes, that is expected. I'd appreciate if you and @minxu74 come up with a fix #258 (personally I'd just drop the calendar months from the results within the REF provider for now). Currently we have a lot of xfails for the integration tests which I'd like to clean up so we can see when the providers don't work as expected. Those integration tests are the most useful tests as they are what users will see.

@lewisjared
Copy link
Copy Markdown
Contributor

Ok lets merge this noting that the data requirements aren't actually what is intended yet ,but that requires the Or operator. I'll follow up with that fix

@lewisjared lewisjared merged commit c953cd2 into main Apr 30, 2025
11 of 12 checks passed
@lewisjared lewisjared deleted the pmp-annual-cycle branch April 30, 2025 00:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implementation of PMP Annual cycle metrics

2 participants