Skip to main content

Developer Quickstart

What you'll learn: Clone the repo, start all services, run your first Unit Commitment solve against the IEEE 118-bus test network, and run your first parametric experiment.

Time: approximately 20 minutes on a developer laptop with Docker already installed.


Prerequisites

ToolRequired versionCheck
Docker + Docker Compose24.0+ / v2.20+docker --version
Python3.12python3 --version
Julia1.10+julia --version
Node.js20 LTSnode --version
pnpm8+pnpm --version
curl + jqanycurl --version && jq --version

Docker is used for PostgreSQL, Redis, and MinIO. Julia and Python run locally for development.


Step 1: Clone and configure

git clone https://github.com/your-org/MangoGridStudio.git
cd MangoGridStudio

Copy the environment template:

cp backend/.env.example backend/.env

The defaults work for local development. Key variables:

DATABASE_URL= # leave blank to use the derived local-dev URL
POSTGRES_USER=mangogrid
POSTGRES_PASSWORD=mangogrid123
POSTGRES_DB=mangogrid_uc
DB_SERVER_PORT=5432
REDIS_URL=redis://localhost:6379/0
JULIA_URL=http://localhost:8082
AUTH_BYPASS_ENABLED=True # disable auth for local dev
ANTHROPIC_API_KEY= # optional; enables AI Analyst and OpenAI fallback route
OPENAI_API_KEY= # optional; tuning_diagnosis auto-reroutes away unless customer acknowledgement is enabled
OPENAI_BASE_URL=
MANGOGRID_OPENAI_CUSTOMER_ACKNOWLEDGED=false

Step 2: Start infrastructure services

cd backend
docker-compose up -d postgres redis minio

Verify all three services are healthy:

docker-compose ps

Expected output — all three should show healthy:

NAME STATUS
MangoGrid_uc_postgres running (healthy)
MangoGrid_uc_redis running (healthy)
MangoGrid_uc_minio running (healthy)

If any show starting, wait 10 seconds and run docker-compose ps again.


Step 3: Install Python dependencies

cd backend
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -r requirements.txt

Run database migrations:

alembic upgrade head

Expected output ends with:

INFO [alembic.runtime.migration] Running upgrade ... -> <revision_id>, ...

Step 4: Start the FastAPI backend

uvicorn app.main:app --host 0.0.0.0 --port 58000 --reload

Verify it is up:

curl -s http://localhost:58000/health | jq .

Expected:

{ "status": "ok" }

Step 5: Start the Julia solver API

In a new terminal:

cd backend/julia_api
julia --project=. build.jl # first run only — downloads and precompiles packages (~5-10 min)
julia --project=. -e 'using MangoGridJuliaAPI; MangoGridJuliaAPI.run()'

The first build.jl step downloads Julia packages (HiGHS, JuMP, UnitCommitment, etc.) and precompiles the system image. This takes 5-10 minutes on first run and is cached for subsequent runs.

Verify:

curl -s http://localhost:8082/health | jq .

Expected:

{ "status": "ok", "version": "0.1.0" }

Step 6: Start the frontend (optional)

In a new terminal:

pnpm install
pnpm dev

The frontend will be available at http://localhost:5173.


Step 7: Run your first UC solve

Use the IEEE 118-bus 24-hour test input from examples/uc_input_ieee118_24h.json.

7a. Submit the job

With AUTH_BYPASS_ENABLED=True, you can skip the login step and use any token:

JOB=$(curl -s -X POST http://localhost:58000/modules/analyst/solve/uc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer dev-bypass-token" \
-d '{
"scenario_id": "quickstart_ieee118",
"payload": '"$(cat examples/uc_input_ieee118_24h.json)"',
"timeout": 120
}' | jq -r .job_id)

echo "Job submitted: $JOB"

7b. Poll for completion

until curl -s http://localhost:58000/modules/analyst/solve/$JOB/status \
-H "Authorization: Bearer dev-bypass-token" | jq -r .status | grep -q completed; do
sleep 2
echo -n "."
done
echo "Solve complete."

Expected: the loop exits after ~4 seconds for the IEEE 118-bus 24-hour problem.

7c. Fetch results

curl -s http://localhost:58000/modules/analyst/solve/$JOB/results \
-H "Authorization: Bearer dev-bypass-token" | jq '{
objective_value,
solve_time_s,
termination_status
}'

Expected output:

{
"objective_value": 142850.50,
"solve_time_s": 3.8,
"termination_status": "OPTIMAL"
}

If you get "termination_status": "INFEASIBLE", the most common causes are:

  • Total generator p_max < peak load. Check that sum of all p_max values exceeds the highest load value.
  • A generator has p_min > p_max. Check generator data.
  • min_uptime or min_downtime constraints conflict with initial_status. See Troubleshooting.

Step 8: Submit your first experiment

Run a fuel price sensitivity sweep over the IEEE 118-bus case:

EXP=$(curl -s -X POST http://localhost:58000/experiments/submit \
-H "Content-Type: application/json" \
-H "Authorization: Bearer dev-bypass-token" \
-d '{
"name": "quickstart_gas_sensitivity",
"base_scenario_id": "quickstart_ieee118",
"scenario_matrix": {
"method": "grid",
"variables": [
{
"name": "gas_price_per_mmbtu",
"type": "continuous",
"min": 2.0,
"max": 6.0,
"steps": 3
}
]
},
"timeout_per_scenario": 60,
"max_parallel": 2
}' | jq -r .experiment_id)

echo "Experiment submitted: $EXP"

Poll progress:

watch -n 2 "curl -s http://localhost:58000/experiments/$EXP/status \
-H 'Authorization: Bearer dev-bypass-token' | jq '{status, scenarios_completed, scenarios_total}'"

Download results as CSV when complete:

curl -s "http://localhost:58000/experiments/$EXP/results/download?format=csv" \
-H "Authorization: Bearer dev-bypass-token" \
-o experiment_results.csv
cat experiment_results.csv

Step 9: Ask the AI Analyst a question

First generate explain artifacts:

curl -s -X POST "http://localhost:58000/modules/analyst/explain/quickstart_ieee118/generate" \
-H "Authorization: Bearer dev-bypass-token" | jq .

Then ask a question:

curl -s -X POST http://localhost:58000/modules/analyst/chat \
-H "Content-Type: application/json" \
-H "Authorization: Bearer dev-bypass-token" \
-d '{
"scenario_id": "quickstart_ieee118",
"message": "Which transmission constraints are binding at the peak hour and what are their shadow prices?"
}' | jq .response

The AI Analyst will call get_binding_constraints automatically and respond in plain English.

Note: the AI Analyst requires an ANTHROPIC_API_KEY environment variable set in backend/.env. If it is not set, the chat endpoint returns 503. Set it with:

echo "ANTHROPIC_API_KEY=sk-ant-..." >> backend/.env

Step 10: Explore the API

Swagger UI

The FastAPI backend generates interactive API documentation automatically. With the backend running, open:

http://localhost:58000/docs

Every endpoint is listed with its request schema, response schema, and a "Try it out" button. No separate tool required.

For a read-only view, ReDoc is at:

http://localhost:58000/redoc

Export the OpenAPI spec

To generate a static backend/openapi.json for use with Postman, code generators, or offline documentation:

python scripts/export_openapi.py

Expected output:

[INFO] Importing FastAPI application ...
[INFO] Generating OpenAPI schema ...
[OK] OpenAPI schema written to backend/openapi.json
[INFO] Total endpoints: 87

To validate the schema and print a grouped endpoint table:

python scripts/export_openapi.py --validate --catalog

This is safe to run without a live database — it extracts the schema from the in-memory FastAPI app.

Load in Postman

  1. Open Postman and select File > Import
  2. Choose File and select backend/openapi.json
  3. Postman generates a full collection with all endpoints pre-configured
  4. Set baseUrl to http://localhost:58000 in the collection variables
  5. Add Authorization: Bearer dev-bypass-token to the collection headers (while AUTH_BYPASS_ENABLED=True)

Concept guides

Before writing production integrations, read the concept guides to understand what the solver is actually doing:

GuideCovers
Unit CommitmentDA-UC objective function, constraint types, IC chaining, Enhanced UC pipeline
LMP DecompositionLMP = energy + congestion + loss (PJM M11 §6.4), PTDF derivation, worked 2-bus example

Using authentication in production

When AUTH_BYPASS_ENABLED=False (the production default):

# Login
TOKEN=$(curl -s -X POST http://localhost:58000/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"your@email.com","password":"yourpassword"}' | jq -r .access_token)

# Use token
curl -s http://localhost:58000/modules/analyst/solve/uc \
-H "Authorization: Bearer $TOKEN" \
...

Using the Python client

The Python solver client provides an async wrapper for the Julia solver API:

import asyncio
import json

# Import the solver client from the backend package
from backend.analyst.solver.client import JuliaSolverClient

async def main():
client = JuliaSolverClient(base_url="http://localhost:8082")
with open("examples/uc_input_ieee118_24h.json") as f:
payload = json.load(f)

job_id = await client.submit_job("uc", payload, timeout=120)
result = await client.wait_for_result(job_id, poll_interval=2.0)
print(f"Objective: {result['objective_value']:.2f}")
print(f"Solve time: {result['solve_time_s']:.1f}s")

asyncio.run(main())

Troubleshooting

"Connection refused" on port 8082

The Julia API takes 2-3 minutes to start on a warm JIT cache. Run curl http://localhost:8082/health every 10 seconds until it responds.

If it never responds, check the Julia terminal for errors. The most common cause is a missing package — run julia --project=. build.jl again.

"INFEASIBLE" solve result

  1. Check that total p_max across all generators exceeds the peak load in all 24 hours.
  2. Check that no generator has p_min > p_max.
  3. Check that initial_status is consistent with min_uptime/min_downtime. For example, a generator with min_uptime=8 and initial_status=1 (online for 1 hour) is valid. A generator with min_downtime=4 and initial_status=-1 (offline for 1 hour) must stay offline for 3 more hours at the start of the solve.

"502 Bad Gateway" from FastAPI

The Julia solver service is not reachable. Check that http://localhost:8082/health returns 200, and that JULIA_API_URL in backend/.env matches the running address.

Alembic "relation does not exist"

The PostgreSQL container is not fully initialized yet. Run docker-compose ps and wait until MangoGrid_uc_postgres shows healthy, then re-run alembic upgrade head.

MinIO connection errors

MinIO can take 15-20 seconds to finish initializing. If the FastAPI backend fails with S3 errors on startup, restart it: uvicorn app.main:app --host 0.0.0.0 --port 58000 --reload.


  • API Reference — authentication and Unit Commitment endpoint documentation
  • Unit Commitment — what the UC solver is actually doing, constraint types, and PJM M11 references
  • LMP Decomposition — how LMP = energy + congestion + loss (PJM M11 §6.4)
  • examples/uc_input_ieee118_24h.json — the canonical test input (54 generators, 118 buses, 24h)