{ "cells": [ { "cell_type": "markdown", "id": "d296e599", "metadata": {}, "source": [ "# Customize a Routine Without Touching the Codebase\n", "\n", "This notebook walks through the supported pattern for adapting a built-in\n", "routine (here `DCOPF`) at runtime — adding parameters, services, variables,\n", "constraints, and extending the objective — using only public API on the\n", "`System` instance.\n", "\n", "The notebook also demonstrates two safety properties of the codegen\n", "pipeline:\n", "\n", "1. **Customizations stay per-instance.** Mutations on `sp2` do not leak\n", " into `sp1` or `sp3`, even though all three share the same disk-cached\n", " pycode at `~/.ams/pycode/dcopf.py`.\n", "2. **You can see which path each item runs through.** The\n", " `routine.formulation_summary()` API tells you exactly what's in\n", " effect — `codegen` (fast AOT path) or `eval` (the eval-fallback\n", " helper, used for runtime customizations)." ] }, { "cell_type": "markdown", "id": "e31fa7ab", "metadata": {}, "source": [ "## Architecture in one paragraph\n", "\n", "`e_str` is the authoring DSL. At first `init()` the codegen\n", "(`ams.prep.generate_for_routine`) compiles each routine's `e_str`\n", "**from a pristine source instance**, never from the user's `sp`,\n", "into named callables that live in `~/.ams/pycode/.py`. The\n", "runtime then either wires those callables onto items (the\n", "`codegen` path) or, for items the user customized at runtime,\n", "falls back to a single eval-fallback helper\n", "(`ams.opt._runtime_eval.eval_e_str`) that resolves bare symbol\n", "names through `RoutineNS`. Both paths produce the same CVXPY\n", "object given the same `e_str`." ] }, { "cell_type": "code", "execution_count": 1, "id": "36eb23ca", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:33.168042Z", "iopub.status.busy": "2026-05-02T08:23:33.167572Z", "iopub.status.idle": "2026-05-02T08:23:34.374362Z", "shell.execute_reply": "2026-05-02T08:23:34.374080Z" } }, "outputs": [], "source": [ "import numpy as np\n", "import ams\n", "\n", "ams.config_logger(stream_level=20) # show INFO so the per-init formulation line prints" ] }, { "cell_type": "markdown", "id": "53cbd284", "metadata": {}, "source": [ "## sp1 — DCOPF, untouched, solve" ] }, { "cell_type": "code", "execution_count": 2, "id": "a803c68e", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.375772Z", "iopub.status.busy": "2026-05-02T08:23:34.375649Z", "iopub.status.idle": "2026-05-02T08:23:34.649767Z", "shell.execute_reply": "2026-05-02T08:23:34.649483Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Working directory: \"/Users/jinningwang/work/ams/examples\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing input file \"/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx\"...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Input file parsed in 0.1174 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Zero Line parameters detected, adjusted to default values: rate_b, rate_c.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "All bus type are PQ, adjusted given load and generator connection status.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "System set up in 0.0019 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Entering data check for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " -> Data check passed\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " formulation: codegen=15/15\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Evaluating OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Finalizing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " initialized in 0.0416 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " solved as optimal in 0.0073 seconds, converged in 9 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp1 = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),\n", " setup=True, no_output=True, default_config=True)\n", "sp1.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "code", "execution_count": 3, "id": "a7853b7d", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.651184Z", "iopub.status.busy": "2026-05-02T08:23:34.651062Z", "iopub.status.idle": "2026-05-02T08:23:34.653027Z", "shell.execute_reply": "2026-05-02T08:23:34.652805Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pg = [0.2 1.43998388 0.6 5.76001612 2. ]\n", "obj = 9.535953244734864\n" ] } ], "source": [ "print('pg =', sp1.DCOPF.pg.v)\n", "print('obj =', float(sp1.DCOPF.obj.v))" ] }, { "cell_type": "code", "execution_count": 4, "id": "52ee8895", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.654185Z", "iopub.status.busy": "2026-05-02T08:23:34.654090Z", "iopub.status.idle": "2026-05-02T08:23:34.656012Z", "shell.execute_reply": "2026-05-02T08:23:34.655735Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " formulation summary (15 codegen / 0 eval / 0 manual / 0 pending)\n", " kind name source e_str\n", " -------- ----- ------- ----------------------------------------\n", " expr plf codegen Bf@aBus + Pfinj\n", " expr pmaxe codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmax)\n", " expr pmine codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmin)\n", " constr pb codegen Bbus@aBus + Pbusinj + Cl@pd + Csh@gsh - Cg@pg == 0\n", " constr sba codegen isb@aBus == 0\n", " constr pglb codegen -pg + pmine <= 0\n", " constr pgub codegen pg - pmaxe <= 0\n", " constr plflb codegen -plf - cp.multiply(ul, rate_a) <= 0\n", " constr plfub codegen plf - cp.multiply(ul, rate_a) <= 0\n", " constr alflb codegen -CftT@aBus + amin <= 0\n", " constr alfub codegen CftT@aBus - amax <= 0\n", " exprcalc pi codegen pb.dual_variables[0]\n", " exprcalc mu1 codegen plflb.dual_variables[0]\n", " exprcalc mu2 codegen plfub.dual_variables[0]\n", " obj obj codegen cp.sum(cp.multiply(c2, pg**2))+ cp.sum(cp.multiply(c1, pg))+\n" ] } ], "source": [ "sp1.DCOPF.formulation_summary()" ] }, { "cell_type": "markdown", "id": "a2839aa1", "metadata": {}, "source": [ "Note: every item shows `codegen` — the fast AOT path is in effect for the\n", "untouched routine. The INFO log line ` formulation: codegen=15/15`\n", "confirms the same." ] }, { "cell_type": "markdown", "id": "aebcf9c6", "metadata": {}, "source": [ "## sp2 — DCOPF, customized, solve\n", "\n", "We extend the routine with an emission balance, an emission cap, and a\n", "small per-unit tax — none of which are in the source code. The\n", "customization is done by `addRParam` / `addService` / `addVars` /\n", "`addConstrs` and a direct `obj.e_str += '...'` append." ] }, { "cell_type": "code", "execution_count": 5, "id": "b4f49c0a", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.657492Z", "iopub.status.busy": "2026-05-02T08:23:34.657373Z", "iopub.status.idle": "2026-05-02T08:23:34.799167Z", "shell.execute_reply": "2026-05-02T08:23:34.798899Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Working directory: \"/Users/jinningwang/work/ams/examples\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing input file \"/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx\"...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Input file parsed in 0.0797 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Zero Line parameters detected, adjusted to default values: rate_b, rate_c.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "All bus type are PQ, adjusted given load and generator connection status.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "System set up in 0.0019 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Entering data check for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " -> Data check passed\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " formulation: codegen=15/15\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Evaluating OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Finalizing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " initialized in 0.0057 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Entering data check for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " -> Data check passed\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " formulation: codegen=14/17, eval(customized)=1, eval(added)=2\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Evaluating OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Finalizing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " initialized in 0.0057 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " solved as optimal in 0.0064 seconds, converged in 12 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp2 = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),\n", " setup=True, no_output=True, default_config=True)\n", "sp2.DCOPF.init() # populate om so .get(...) works\n", "\n", "stg_idx = sp2.DCOPF.pg.get_all_idxes()\n", "pmax = sp2.DCOPF.get(src='pmax', attr='v', idx=stg_idx)\n", "\n", "# Heterogeneous emission factor — proportional to 1/pmax (a stand-in\n", "# for: small/peaker units are less efficient and emit more per p.u.).\n", "ke = np.reciprocal(pmax)\n", "# Per-unit tax — also 1/pmax.\n", "ce = np.reciprocal(pmax)\n", "\n", "sp2.DCOPF.addRParam(name='ke', tex_name='k_e', info='emission factor', v=ke)\n", "sp2.DCOPF.addRParam(name='ce', tex_name='c_e', info='per-unit tax', v=ce)\n", "# Cap chosen so the eub constraint actually binds. The baseline solution has\n", "# sum(ke @ pg) ~ 1.53 — picking te = 1.0 forces ~35% redistribution.\n", "sp2.DCOPF.addService(name='te', tex_name='t_e', info='emission cap', value=1.0)\n", "sp2.DCOPF.addVars(name='eg', tex_name='e_g', info='gen emission',\n", " unit='t', model='StaticGen')\n", "# is_eq retired in v1.2.3 — embed the relational operator in e_str\n", "# directly. LHS-zero discipline keeps `.v` reporting slack-from-zero\n", "# (negative = respected, positive = violated) regardless of `<=`/`>=`\n", "# direction (CVXPY canonicalizes both to `lhs - rhs <= 0` internally).\n", "sp2.DCOPF.addConstrs(name='egb', info='emission balance',\n", " e_str='eg - cp.multiply(ke, pg) == 0')\n", "sp2.DCOPF.addConstrs(name='eub', info='emission cap',\n", " e_str='cp.sum(eg) - te <= 0')\n", "sp2.DCOPF.obj.e_str += '+ cp.sum(cp.multiply(ce, pg))'\n", "\n", "sp2.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "code", "execution_count": 6, "id": "90ef5387", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.800510Z", "iopub.status.busy": "2026-05-02T08:23:34.800321Z", "iopub.status.idle": "2026-05-02T08:23:34.802403Z", "shell.execute_reply": "2026-05-02T08:23:34.802203Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pg = [0.2 2.11651035 0.6 6.41765794 0.66583171]\n", "obj = 11.297128566649729\n" ] } ], "source": [ "print('pg =', sp2.DCOPF.pg.v)\n", "print('obj =', float(sp2.DCOPF.obj.v))" ] }, { "cell_type": "code", "execution_count": 7, "id": "2665d6ff", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.803559Z", "iopub.status.busy": "2026-05-02T08:23:34.803456Z", "iopub.status.idle": "2026-05-02T08:23:34.805266Z", "shell.execute_reply": "2026-05-02T08:23:34.805042Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " formulation summary (14 codegen / 3 eval / 0 manual / 0 pending)\n", " kind name source e_str\n", " -------- ----- ------- ----------------------------------------\n", " expr plf codegen Bf@aBus + Pfinj\n", " expr pmaxe codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmax)\n", " expr pmine codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmin)\n", " constr pb codegen Bbus@aBus + Pbusinj + Cl@pd + Csh@gsh - Cg@pg == 0\n", " constr sba codegen isb@aBus == 0\n", " constr pglb codegen -pg + pmine <= 0\n", " constr pgub codegen pg - pmaxe <= 0\n", " constr plflb codegen -plf - cp.multiply(ul, rate_a) <= 0\n", " constr plfub codegen plf - cp.multiply(ul, rate_a) <= 0\n", " constr alflb codegen -CftT@aBus + amin <= 0\n", " constr alfub codegen CftT@aBus - amax <= 0\n", " constr egb eval eg - cp.multiply(ke, pg) == 0\n", " constr eub eval cp.sum(eg) - te <= 0\n", " exprcalc pi codegen pb.dual_variables[0]\n", " exprcalc mu1 codegen plflb.dual_variables[0]\n", " exprcalc mu2 codegen plfub.dual_variables[0]\n", " obj obj eval cp.sum(cp.multiply(c2, pg**2))+ cp.sum(cp.multiply(c1, pg))+\n" ] } ], "source": [ "sp2.DCOPF.formulation_summary()" ] }, { "cell_type": "markdown", "id": "ecfead40", "metadata": {}, "source": [ "Three items are now `eval` (the eval-fallback helper):\n", "\n", "- `egb` and `eub` were added at runtime via `addConstrs` — the disk\n", " pycode doesn't know about them, so they fall through.\n", "- `obj` was customized via `obj.e_str += '...'`. The descriptor mutex\n", " marked it `_e_dirty`, so `_link_pycode` skipped wiring its codegen\n", " callable.\n", "\n", "Everything else stays on `codegen`." ] }, { "cell_type": "markdown", "id": "d99135af", "metadata": {}, "source": [ "## sp3 — DCOPF, untouched, solve\n", "\n", "This is the contamination check: did `sp2`'s customization leak into the\n", "disk cache? If it did, `sp3` would inherit `sp2`'s formulation." ] }, { "cell_type": "code", "execution_count": 8, "id": "080b56ac", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.806553Z", "iopub.status.busy": "2026-05-02T08:23:34.806445Z", "iopub.status.idle": "2026-05-02T08:23:34.888668Z", "shell.execute_reply": "2026-05-02T08:23:34.888421Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Working directory: \"/Users/jinningwang/work/ams/examples\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing input file \"/Users/jinningwang/work/ams/ams/cases/5bus/pjm5bus_demo.xlsx\"...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Input file parsed in 0.0315 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Zero Line parameters detected, adjusted to default values: rate_b, rate_c.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "All bus type are PQ, adjusted given load and generator connection status.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "System set up in 0.0019 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Entering data check for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " -> Data check passed\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Building system matrices\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " formulation: codegen=15/15\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Parsing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Evaluating OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "Finalizing OModel for \n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " initialized in 0.0052 seconds.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ " solved as optimal in 0.0050 seconds, converged in 9 iterations with CLARABEL.\n" ] }, { "data": { "text/plain": [ "True" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp3 = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),\n", " setup=True, no_output=True, default_config=True)\n", "sp3.DCOPF.run(solver='CLARABEL')" ] }, { "cell_type": "code", "execution_count": 9, "id": "0ff00f4e", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.889886Z", "iopub.status.busy": "2026-05-02T08:23:34.889796Z", "iopub.status.idle": "2026-05-02T08:23:34.891671Z", "shell.execute_reply": "2026-05-02T08:23:34.891468Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "pg = [0.2 1.43998388 0.6 5.76001612 2. ]\n", "obj = 9.535953244734864\n" ] } ], "source": [ "print('pg =', sp3.DCOPF.pg.v)\n", "print('obj =', float(sp3.DCOPF.obj.v))" ] }, { "cell_type": "code", "execution_count": 10, "id": "200d1fd0", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.892759Z", "iopub.status.busy": "2026-05-02T08:23:34.892677Z", "iopub.status.idle": "2026-05-02T08:23:34.894532Z", "shell.execute_reply": "2026-05-02T08:23:34.894322Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " formulation summary (15 codegen / 0 eval / 0 manual / 0 pending)\n", " kind name source e_str\n", " -------- ----- ------- ----------------------------------------\n", " expr plf codegen Bf@aBus + Pfinj\n", " expr pmaxe codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmax)\n", " expr pmine codegen cp.multiply(nctrle, pg0) + cp.multiply(ctrle, pmin)\n", " constr pb codegen Bbus@aBus + Pbusinj + Cl@pd + Csh@gsh - Cg@pg == 0\n", " constr sba codegen isb@aBus == 0\n", " constr pglb codegen -pg + pmine <= 0\n", " constr pgub codegen pg - pmaxe <= 0\n", " constr plflb codegen -plf - cp.multiply(ul, rate_a) <= 0\n", " constr plfub codegen plf - cp.multiply(ul, rate_a) <= 0\n", " constr alflb codegen -CftT@aBus + amin <= 0\n", " constr alfub codegen CftT@aBus - amax <= 0\n", " exprcalc pi codegen pb.dual_variables[0]\n", " exprcalc mu1 codegen plflb.dual_variables[0]\n", " exprcalc mu2 codegen plfub.dual_variables[0]\n", " obj obj codegen cp.sum(cp.multiply(c2, pg**2))+ cp.sum(cp.multiply(c1, pg))+\n" ] } ], "source": [ "sp3.DCOPF.formulation_summary()" ] }, { "cell_type": "markdown", "id": "751318d1", "metadata": {}, "source": [ "## Verify the two invariants" ] }, { "cell_type": "code", "execution_count": 11, "id": "e56c6dfb", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.895522Z", "iopub.status.busy": "2026-05-02T08:23:34.895461Z", "iopub.status.idle": "2026-05-02T08:23:34.897677Z", "shell.execute_reply": "2026-05-02T08:23:34.897472Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sp1.pg == sp3.pg ? PASS\n", "sp1.pg != sp2.pg ? PASS\n", "sp1.obj == sp3.obj ? PASS\n" ] } ], "source": [ "same_13 = np.allclose(sp1.DCOPF.pg.v, sp3.DCOPF.pg.v, atol=1e-8)\n", "diff_12 = not np.allclose(sp1.DCOPF.pg.v, sp2.DCOPF.pg.v, atol=1e-6)\n", "same_obj_13 = abs(float(sp1.DCOPF.obj.v) - float(sp3.DCOPF.obj.v)) < 1e-6\n", "\n", "print(f'sp1.pg == sp3.pg ? {\"PASS\" if same_13 else \"FAIL\"}')\n", "print(f'sp1.pg != sp2.pg ? {\"PASS\" if diff_12 else \"FAIL\"}')\n", "print(f'sp1.obj == sp3.obj ? {\"PASS\" if same_obj_13 else \"FAIL\"}')" ] }, { "cell_type": "markdown", "id": "e97f508e", "metadata": {}, "source": [ "## How to know which path your customization is using\n", "\n", "At any point after `init()`:\n", "\n", "- `routine.formulation_summary()` — full per-item table.\n", "- `item.formulation_source` — single-item check, returns one of\n", " `'codegen' | 'eval' | 'manual' | 'pending'`.\n", "- The INFO log line `<{routine}> formulation: codegen=X/Y, eval(...)=Z`\n", " prints on every `init()`.\n", "\n", "If you customized something but its source still reads `codegen`, the\n", "customization didn't take effect." ] }, { "cell_type": "code", "execution_count": 12, "id": "c768505d", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.898684Z", "iopub.status.busy": "2026-05-02T08:23:34.898610Z", "iopub.status.idle": "2026-05-02T08:23:34.900453Z", "shell.execute_reply": "2026-05-02T08:23:34.900252Z" } }, "outputs": [ { "data": { "text/plain": [ "'eval'" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp2.DCOPF.obj.formulation_source" ] }, { "cell_type": "code", "execution_count": 13, "id": "d329d0f8", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.901481Z", "iopub.status.busy": "2026-05-02T08:23:34.901417Z", "iopub.status.idle": "2026-05-02T08:23:34.903259Z", "shell.execute_reply": "2026-05-02T08:23:34.903028Z" } }, "outputs": [ { "data": { "text/plain": [ "'eval'" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp2.DCOPF.constrs['egb'].formulation_source" ] }, { "cell_type": "code", "execution_count": 14, "id": "4ed99879", "metadata": { "execution": { "iopub.execute_input": "2026-05-02T08:23:34.904390Z", "iopub.status.busy": "2026-05-02T08:23:34.904306Z", "iopub.status.idle": "2026-05-02T08:23:34.906277Z", "shell.execute_reply": "2026-05-02T08:23:34.906072Z" } }, "outputs": [ { "data": { "text/plain": [ "'codegen'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sp2.DCOPF.constrs['pb'].formulation_source" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 5 }