Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5dbe787
first try
klamike Aug 2, 2025
7fc30af
use julia cache
klamike Aug 2, 2025
c920f34
Add installation instructions to slurm
klamike Aug 3, 2025
d771148
Add exp install to readme
klamike Aug 3, 2025
213e47a
put PackageCompiler in exp instead of slurm
klamike Aug 3, 2025
cba6ac2
only exp deps in image
klamike Aug 3, 2025
a07bf22
submit -> create
klamike Aug 3, 2025
5e467af
multithreaded sysimage compilation
klamike Aug 3, 2025
fa41968
add merge deps to slurm
klamike Aug 3, 2025
e61ad90
test lts
klamike Aug 3, 2025
ea0b5d8
typo
klamike Aug 3, 2025
134cd5b
[skip CI] typo
klamike Aug 3, 2025
baf5b66
update install instructions for ≤1.10 and HSL_jll
klamike Aug 15, 2025
6b2e9d5
Update slurm/make_sysimage.jl
klamike Aug 15, 2025
db72799
ED is linear so no need for maybe_set_ad
klamike Aug 15, 2025
5708683
put it back in case using nlp solver
klamike Aug 15, 2025
eb3f0b9
help msg
klamike Oct 8, 2025
fc3377a
update base config
klamike Oct 8, 2025
d255eb1
Merge branch 'main' into mk/refactordeps
klamike Dec 17, 2025
17579fa
Don't test lts
klamike Dec 17, 2025
68d0376
Update .github/workflows/ci.yml
klamike Dec 17, 2025
91cdd2a
Consolidate to single unified Project.toml (#214)
Copilot Dec 17, 2025
9f043c8
Merge branch 'main' into mk/refactordeps
klamike Dec 18, 2025
0c423e4
Apply suggestion from @klamike
klamike Dec 18, 2025
1134bdf
support lts
klamike Jan 24, 2026
58adc76
only test latest on CI
klamike Jan 24, 2026
068118b
Apply suggestion from @klamike
klamike Jan 24, 2026
2df8c41
fix sysimage
klamike Jan 24, 2026
0bb0aa2
less verbose time limit warning
klamike Jan 24, 2026
40673e0
detect hsl
klamike Jan 24, 2026
614768a
use user julia instead of lmod's by default
klamike Jan 24, 2026
bc28c07
default time_limit to Inf
klamike Jan 24, 2026
87a2e30
use warn macro
klamike Jan 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,7 @@ jobs:
with:
version: ${{ matrix.version }}
Comment thread
klamike marked this conversation as resolved.
arch: ${{ matrix.arch }}
- uses: actions/cache@v5
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
Expand Down
1 change: 0 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MKL = "33e6dc65-8f57-5167-99aa-e5a354878fb2"
MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee"
MathOptSymbolicAD = "309f4015-3481-4d63-a8f9-aeb13adfe8eb"
Mosek = "6405355b-0ac2-5fba-af84-adbd65488c0e"
MosekTools = "1ec41992-ff65-5c91-ac43-2df89e9693a4"
Mustache = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70"
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ dataset = [
## Generating datasets

A script for generating multiple ACOPF instances is given in [`exp/sampler.jl`](exp/sampler.jl).
Make sure to install the required dependencies first:
```bash
# cd path/to/PGLearn.jl
julia --project=. -e "using Pkg; Pkg.instantiate()"
```

It is called from the command-line as follows:
```bash
Expand Down
8 changes: 4 additions & 4 deletions exp/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ solver.attributes.iterative_refinement_abstol = 1e-18

[slurm]
# SLURM options
n_samples = 65536
n_jobs = 45
queue = "embers"
charge_account = "gts-phentenryck3-ai4opt"
n_samples = 100
n_jobs = 5
queue = "<qos here>"
charge_account = "<charge account here>"
19 changes: 10 additions & 9 deletions exp/sampler.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,9 @@ using Mosek, MosekTools
using HiGHS

using HSL_jll
const HAS_REAL_HSL = (@ccall HSL_jll.libhsl.LIBHSL_isfunctional()::Bool)
const LIB_COINHSL = HSL_jll.libhsl_path

using MathOptSymbolicAD

using PGLearn

const DEFAULT_FLOAT_PRECISION = Float32
Expand Down Expand Up @@ -68,15 +67,19 @@ function get_time_limits(config)
return time_limits
end

function build_and_solve_model(data, config, dataset_name; time_limit=nothing)
function build_and_solve_model(data, config, dataset_name; time_limit=Inf)
opf_config = config["OPF"][dataset_name]
OPF = PGLearn.OPF2TYPE[opf_config["type"]]
solver_config = get(opf_config, "solver", Dict())

if solver_config["name"] == "Ipopt"
# Make sure we provide an HSL path
get!(solver_config, "attributes", Dict())
get!(solver_config["attributes"], "hsllib", HSL_jll.libhsl_path)
if !HAS_REAL_HSL
@warn "Ignoring HSL linear solver since the installed HSL_jll is dummy. See the Ipopt.jl README for how to install HSL."
else
get!(solver_config, "attributes", Dict())
get!(solver_config["attributes"], "hsllib", HSL_jll.libhsl_path)
end
end

solver = optimizer_with_attributes(NAME2OPTIMIZER[solver_config["name"]],
Expand All @@ -92,11 +95,9 @@ function build_and_solve_model(data, config, dataset_name; time_limit=nothing)
set_silent(opf.model)
# Set time limit if one is not already set
current_time_limit = JuMP.time_limit_sec(opf.model)
if isnothing(current_time_limit) || !isfinite(current_time_limit)
@info "setting time limit to $time_limit"
if (isnothing(current_time_limit) || !isfinite(current_time_limit)) && time_limit != current_time_limit
@info "setting time limit to $time_limit, was $current_time_limit"
JuMP.set_time_limit_sec(opf.model, time_limit)
else
@info "skipping time limit since it is already $(JuMP.time_limit_sec(opf.model))"
end

PGLearn.solve!(opf)
Expand Down
24 changes: 20 additions & 4 deletions slurm/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PGLearn SLURM Job Submission and Output Guide

This guide provides instructions on how to use the `submit_jobs.jl` script to submit jobs to a SLURM HPC cluster for generating OPF problem instances with PGLearn, as well as details on the output structure.
This guide provides instructions on how to use the `create_jobs.jl` script to submit jobs to a SLURM HPC cluster for generating OPF problem instances with PGLearn, as well as details on the output structure.

The script has three dependencies: `julia`, `gcc` (for sysimage), and `parallel` (for sampler). The file `env.sh` in the `slurm/template` directory contains a default environment setup which may need to be changed by the user. The user can either edit this file directly or create a new one and provide the path to it in the configuration file with the `slurm.env_path` option. The script will be sourced (executed in the job's main shell) before running each of the jobs.

Expand All @@ -24,12 +24,28 @@ Create a TOML configuration file with the following options:
| `slurm.julia_bin` | Julia command to use | No | `julia --sysimage=app/julia.so` |
| `slurm.env_path` | Path to env script | No | `slurm/template/env.sh` |


## Setup

First, make sure to install all dependencies:
```bash
# cd path/to/PGLearn.jl
julia --project=. -e "using Pkg; Pkg.instantiate()"
```

If you're using the HSL linear solvers with Ipopt, make sure to `dev` the `HSL_jll.jl` package you downloaded:
```bash
julia --project=. -e "using Pkg; Pkg.develop(path=\"path/to/HSL_jll.jl\"); Pkg.precompile()"
```

Then, make sure the `slurm/template/env.sh` file is set up correctly for your environment. You can either edit this file directly or create a new one and specify its path in the configuration file.

## Usage

1. Create a TOML configuration file with the required options.
2. Run the `submit_jobs.jl` script with the path to the configuration file:
2. Run the `create_jobs.jl` script with the path to the configuration file:
```bash
julia --project=. slurm/submit_jobs.jl path/to/config.toml
julia --project=. slurm/create_jobs.jl path/to/config.toml
```
3. Follow the printed instructions to submit the jobs to the SLURM queue.
4. When submitting slurm jobs, you will be prompted to select whether to (re)-create a julia sysimage
Expand All @@ -51,7 +67,7 @@ If you decide to (re)create a sysimage, a specific job will be submitted to the

The script will organize the intermediate files into the following directories within the specified `export_dir`:

- `slurm`: Contains generated SLURM job files and submission scripts created by the `submit_jobs.jl` script.
- `slurm`: Contains generated SLURM job files and submission scripts created by the `create_jobs.jl` script.
- `res_json`: Stores JSON files with individual instance and solution data created during the sampler job.
- `res_h5`: Stores semi-aggregated HDF5 files created during the extract job.

Expand Down
File renamed without changes.
28 changes: 10 additions & 18 deletions slurm/make_sysimage.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,21 @@ using PackageCompiler
# first run `julia --project=. -t1 --trace-compile=app/precompile.jl exp/sampler.jl <path/to/config.toml> 1 1`
create_sysimage(
[
"PGLearn",
"Random",
"Distributions",
"PowerModels",
"PGLib",
"JuMP",
"Graphs",
"ProgressMeter",
"HDF5",
"Clarabel",
"Quadmath",
"HSL_jll",
"HiGHS",
"Ipopt",
"JuMP",
"LinearAlgebra",
"MKL",
"Mosek",
"MosekTools",
"HSL_jll",
"LinearAlgebra",
"MathOptSymbolicAD",
"PGLearn",
"PGLib",
"PowerModels",
"Quadmath",
"Random",
"TOML",
"CodecBzip2",
"CodecZlib",
"JSON",
"MKL",
"SparseArrays"
];
Comment thread
klamike marked this conversation as resolved.
sysimage_path="app/julia.so",
precompile_statements_file="app/precompile.jl"
Expand Down
2 changes: 1 addition & 1 deletion slurm/template/env.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## add commands to this file that make gcc, parallel, and julia available.

## if using lmod, grab the latest versions
module load gcc parallel julia
module load gcc parallel

## if using bashrc, source it
# source $HOME/.bashrc
Expand Down
4 changes: 2 additions & 2 deletions slurm/template/sysimage.sbatch
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#SBATCH --account={{:charge_account}} # charge account
#SBATCH --nodes=1 # Use one node
#SBATCH --ntasks=1 # Run a single task
#SBATCH --cpus-per-task=1 # Give 1 CPU
#SBATCH --cpus-per-task=8 # Give 8 CPUs
#SBATCH --mem-per-cpu={{:sysimage_memory}} # Memory per processor
#SBATCH --time=01:00:00 # Time limit hrs:min:sec
#SBATCH -o {{{:logs_dir}}}/sysimage.out # Combined output and error messages file
Expand All @@ -17,4 +17,4 @@ mkdir app

julia --project=. -t1 --trace-compile=app/precompile.jl {{{:sampler_script}}} {{{:config_file}}} 1 1

julia --project=. slurm/make_sysimage.jl
julia --project=. -t8 slurm/make_sysimage.jl
3 changes: 2 additions & 1 deletion src/opf/ed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ end


function solve!(opf::OPFModel{EconomicDispatch})
maybe_set_ad(opf)
model = opf.model

data = opf.data
Expand Down Expand Up @@ -158,7 +159,7 @@ function solve!(opf::OPFModel{EconomicDispatch})
t0 = time()
while !solved && niter < model.ext[:solve_metadata][:max_ptdf_iterations]
# Solve model
optimize!(opf.model, _differentiation_backend = MathOptSymbolicAD.DefaultBackend())
optimize!(opf.model)

# Exit if not solved optimally
st = termination_status(model)
Expand Down
11 changes: 9 additions & 2 deletions src/opf/opf.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using MathOptSymbolicAD
using SparseArrays

abstract type AbstractFormulation end
Expand Down Expand Up @@ -358,5 +357,13 @@ function build_opf(OPF::Type{<:AbstractFormulation}, network::Dict, optimizer; c
end

function solve!(opf::OPFModel{<:AbstractFormulation})
optimize!(opf.model; _differentiation_backend = MathOptSymbolicAD.DefaultBackend())
maybe_set_ad(opf)
optimize!(opf.model)
end
function maybe_set_ad(opf::OPFModel{<:AbstractFormulation})
!isnothing(nonlinear_model(opf.model)) && set_attribute(
opf.model,
MOI.AutomaticDifferentiationBackend(),
MOI.Nonlinear.SymbolicMode(),
)
end
6 changes: 5 additions & 1 deletion test/utils/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ function test_h5_invalid_types()

# Not converting JuMP status codes to String is a common mistake...
st = MOI.OPTIMAL
msg = "Unsupported data type for writing to an HDF5 group: \"termination_status\"::TerminationStatusCode."
msg = if VERSION < v"1.12" && pkgversion(JuMP) ≥ v"0.22"
"Unsupported data type for writing to an HDF5 group: \"termination_status\"::MathOptInterface.TerminationStatusCode."
else
"Unsupported data type for writing to an HDF5 group: \"termination_status\"::TerminationStatusCode."
end
@test_throws msg PGLearn.save_h5(f5, Dict("termination_status" => st))

return nothing
Expand Down