From 84fdbf2f1d64df185c8163b09eec4c87a040454d Mon Sep 17 00:00:00 2001 From: Cameron Rutherford Date: Tue, 21 May 2024 13:19:00 -0400 Subject: [PATCH] TCOPFLOW Python Bindings First Pass (#131) * add new branch and pytests repo * add tecopflow files * clean dir and add new tcopflow files * Apply pre-commmit fixes * add tcopflow files to python bindings * rename python files from test to example * Apply pre-commmit fixes * rebase on origin branch * test_tcopflow.cpp * sort tcopflow functions * Apply pre-commmit fixes * test tcopflow functions * first passing tcopflow test * Apply pre-commmit fixes * clean tcopflow test file * Apply pre-commmit fixes * file clean up and comment added to test_tcopflow.py * removed tcopflow_bind files * add examples to example_tcopflow.pu * Apply pre-commmit fixes * remove u unused value on test_5_tcopflow.py * Apply pre-commmit fixes * checked preconditions before importing exago on test_5_tcopflow.py * check preconditions for all tests * Apply pre-commmit fixes * documented tcopflow function on python_bindings.md file and tested the functions * Added functions to exago_python_tcopflow.cpp and updated the documentation * Apply pre-commmit fixes * checked preconditions() before importing exago * fix pre-commit checks * Apply pre-commmit fixes * check preconditions and import exago * check preconditions and import exago on test_9_finalize.py * Apply pre-commmit fixes * remove duplicate check_preconditions() on test_0_initialize.py * import exago after pre_conditions check --------- Co-authored-by: Mohan, Jaya Co-authored-by: cameronrutherford Co-authored-by: Jaelyn Litzinger Co-authored-by: jaelynlitz Co-authored-by: Jaya Mohan Co-authored-by: Jaya Mohan Co-authored-by: Jaya Mohan Co-authored-by: Jayapreethi Co-authored-by: Jaya Mohan --- docs/python_bindings.md | 28 +++- interfaces/python/CMakeLists.txt | 6 +- interfaces/python/exago_python.cpp | 4 + interfaces/python/exago_python_tcopflow.cpp | 141 ++++++++++++++++++ interfaces/python/exago_python_tcopflow.hpp | 8 + .../{test_opflow.py => example_opflow.py} | 0 .../{test_pflow.py => example_pflow.py} | 0 .../{test_scopflow.py => example_scopflow.py} | 0 .../{test_sopflow.py => example_sopflow.py} | 0 interfaces/python/example_tcopflow.py | 17 +++ tests/interfaces/python/CMakeLists.txt | 11 +- tests/interfaces/python/test_0_initialize.py | 6 +- tests/interfaces/python/test_1_pflow.py | 5 +- tests/interfaces/python/test_2_opflow.py | 6 +- tests/interfaces/python/test_3_scopflow.py | 4 +- tests/interfaces/python/test_4_sopflow.py | 4 +- tests/interfaces/python/test_5_tcopflow.py | 37 +++++ tests/interfaces/python/test_9_finalize.py | 3 +- 18 files changed, 259 insertions(+), 21 deletions(-) create mode 100644 interfaces/python/exago_python_tcopflow.cpp create mode 100644 interfaces/python/exago_python_tcopflow.hpp rename interfaces/python/{test_opflow.py => example_opflow.py} (100%) rename interfaces/python/{test_pflow.py => example_pflow.py} (100%) rename interfaces/python/{test_scopflow.py => example_scopflow.py} (100%) rename interfaces/python/{test_sopflow.py => example_sopflow.py} (100%) create mode 100644 interfaces/python/example_tcopflow.py create mode 100644 tests/interfaces/python/test_5_tcopflow.py diff --git a/docs/python_bindings.md b/docs/python_bindings.md index 054b0fabe..543dc7efb 100644 --- a/docs/python_bindings.md +++ b/docs/python_bindings.md @@ -178,7 +178,7 @@ The following table assumes `opflow = exago.OPFLOW()`. | `OPFLOWReadMatPowerData` | `opflow.read_mat_power_data` | | | `OPFLOWSolutionToPS` | `opflow.solution_to_ps` | | | `OPFLOWSetUpPS` | `opflow.set_up_ps` | | -| `OPFLOWSkipOptions` | `opflow.skip_options | | +| `OPFLOWSkipOptions` | `opflow.skip_options` | | | `OPFLOWSetLinesMonitored` | | Implemented as two different methods: | | | |`opflow.set_lines_monitored([...])` | Specify a list of line kvlevels (type `float`) to monitor | | |`opflow.set_lines_monitored(n, "file")` | Read `n` line kvlevels from a file (`n=-1` for all). | @@ -267,6 +267,32 @@ The following table assumes `sopflow = exago.SOPFLOW()`. | ` SOPFLOWSaveSolution` | `save_solution` | | | ` SOPFLOWSaveSolutionAll` | `save_solution_all` | | +#### TCOPFLOW + +The following table assumes `tcopflow = exago.TCOPFLOW()`. + +| C++ API | Python API | Notes | +|---|---|---| +| `TCOPFLOW` | `exago.TCOPFLOW` class | | +| ` TCOPFLOWSetModel` | `set_model` | | +| ` TCOPFLOWSetNetworkData` | `set_network_data` | | +| ` TCOPFLOWSetSolver` | `set_solver` | | +| ` TCOPFLOWSetTolerance` | `set_tolerance` | | +| ` TCOPFLOWSetTimeStepandDuration` | `set_timestep_and_duration` | | +| ` TCOPFLOWSetLoadProfiles` | `set_load_profiles` | | +| ` TCOPFLOWSetWindGenProfiles` | `wind_gen_profiles` | | +| ` TCOPFLOWSetup` | `setup` | | +| ` TCOPFLOWSolve` | `solve` | | +| ` TCOPFLOWGetConvergenceStatus` | `get_convergence_status` | | +| ` TCOPFLOWGetObjective` | `get_objective` | | +| ` TCOPFLOWGetNumIterations` | `get_num_iterations` | | +| ` TCOPFLOWSetTolerance` | `set_tolerance` | | +| ` TCOPFLOWGetTolerance` | `get_tolerance` | | +| ` TCOPFLOWPrintSolution` | `print_solution` | | +| ` TCOPFLOWSaveSolution` | `save_solution` | | +| ` TCOPFLOWSavesolutionAll` | `save_solution_all` || + + ### Enums `OutputFormat` is the enum that specifies the output format in functions like `opflow.save_solution`. diff --git a/interfaces/python/CMakeLists.txt b/interfaces/python/CMakeLists.txt index 8f410aa86..5bbefcc95 100644 --- a/interfaces/python/CMakeLists.txt +++ b/interfaces/python/CMakeLists.txt @@ -18,8 +18,10 @@ if(EXAGO_ENABLE_IPOPT OR EXAGO_ENABLE_HIOP) endif() if(EXAGO_ENABLE_IPOPT) - list(APPEND EXAGO_PY_SRCS exago_python_scopflow.cpp exago_python_sopflow.cpp) - list(APPEND EXAGO_PY_LIBS ExaGO::SOPFLOW ExaGO::SCOPFLOW) + list(APPEND EXAGO_PY_SRCS exago_python_scopflow.cpp exago_python_sopflow.cpp + exago_python_tcopflow.cpp + ) + list(APPEND EXAGO_PY_LIBS ExaGO::SOPFLOW ExaGO::SCOPFLOW ExaGO::TCOPFLOW) endif() # New true python bindings diff --git a/interfaces/python/exago_python.cpp b/interfaces/python/exago_python.cpp index e3cdc3c90..13905fab5 100644 --- a/interfaces/python/exago_python.cpp +++ b/interfaces/python/exago_python.cpp @@ -64,5 +64,9 @@ PYBIND11_MODULE(exago, m) { extern void init_exago_sopflow(pybind11::module & m); init_exago_sopflow(m); + + extern void init_exago_tcopflow(pybind11::module & m); + init_exago_tcopflow(m); + #endif } diff --git a/interfaces/python/exago_python_tcopflow.cpp b/interfaces/python/exago_python_tcopflow.cpp new file mode 100644 index 000000000..886589cdd --- /dev/null +++ b/interfaces/python/exago_python_tcopflow.cpp @@ -0,0 +1,141 @@ +#include "exago_python_tcopflow.hpp" + +// ------------------------------------------------------------- +// class TCOPFLOW_wrapper +// +// Wrap to make sure allocation and destruction is handled correctly +// ------------------------------------------------------------- +class TCOPFLOW_wrapper { +public: + TCOPFLOW tcopf; + TCOPFLOW_wrapper(void) { + PetscErrorCode ierr; + MPI_Comm communicator; + ierr = ExaGOGetSelfCommunicator(&communicator); + ExaGOCheckError(ierr); + ierr = TCOPFLOWCreate(communicator, &tcopf); + ExaGOCheckError(ierr); + } + ~TCOPFLOW_wrapper(void) { + PetscErrorCode ierr; + ierr = TCOPFLOWDestroy(&tcopf); + ExaGOCheckError(ierr); + } +}; + +// ------------------------------------------------------------- +// init_exago_tcopflow +// ------------------------------------------------------------- +void init_exago_tcopflow(pybind11::module &m) { + + pybind11::class_(m, "TCOPFLOW") + .def(pybind11::init()) + .def("set_network_data", + [](TCOPFLOW_wrapper &w, std::string filename) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetNetworkData(w.tcopf, filename.c_str()); + ExaGOCheckError(ierr); + }) + .def("set_solver", + [](TCOPFLOW_wrapper &w, std::string solver) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetSolver(w.tcopf, solver.c_str()); + ExaGOCheckError(ierr); + }) + .def("set_tolerance", + [](TCOPFLOW_wrapper &w, double tol) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetTolerance(w.tcopf, tol); + ExaGOCheckError(ierr); + }) + .def("setup", + [](TCOPFLOW_wrapper &w) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetUp(w.tcopf); + ExaGOCheckError(ierr); + }) + .def("set_model", + [](TCOPFLOW_wrapper &w, std::string model) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetModel(w.tcopf, model.c_str()); + ExaGOCheckError(ierr); + }) + + .def("solve", + [](TCOPFLOW_wrapper &w) { + PetscErrorCode ierr; + ierr = TCOPFLOWSolve(w.tcopf); + ExaGOCheckError(ierr); + }) + .def("set_time_step_and_duration", + [](TCOPFLOW_wrapper &w, double dt, double tmax) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetTimeStepandDuration(w.tcopf, dt, tmax); + ExaGOCheckError(ierr); + }) + .def("set_load_profiles", + [](TCOPFLOW_wrapper &w, const std::string &pload, + const std::string &qload) { + PetscErrorCode ierr; + ierr = + TCOPFLOWSetLoadProfiles(w.tcopf, pload.c_str(), qload.c_str()); + ExaGOCheckError(ierr); + }) + .def("set_wind_gen_profiles", + [](TCOPFLOW_wrapper &w, std::string filename) { + PetscErrorCode ierr; + ierr = TCOPFLOWSetWindGenProfiles(w.tcopf, filename.c_str()); + ExaGOCheckError(ierr); + }) + .def("get_convergence_status", + [](TCOPFLOW_wrapper &w) -> bool { + PetscErrorCode ierr; + PetscBool flag; + ierr = TCOPFLOWGetConvergenceStatus(w.tcopf, &flag); + ExaGOCheckError(ierr); + return flag; + }) + .def("get_objective", + [](TCOPFLOW_wrapper &w) -> double { + PetscErrorCode ierr; + double obj; + ierr = TCOPFLOWGetObjective(w.tcopf, &obj); + ExaGOCheckError(ierr); + return obj; + }) + .def("get_num_iterations", + [](TCOPFLOW_wrapper &w) -> int { + PetscErrorCode ierr; + PetscInt n; + ierr = TCOPFLOWGetNumIterations(w.tcopf, &n); + ExaGOCheckError(ierr); + return n; + }) + .def("get_tolerance", + [](TCOPFLOW_wrapper &w) -> double { + PetscErrorCode ierr; + double tol; + ierr = TCOPFLOWGetTolerance(w.tcopf, &tol); + ExaGOCheckError(ierr); + return tol; + }) + .def("print_solution", + [](TCOPFLOW_wrapper &w, int isol) { + PetscErrorCode ierr; + ierr = TCOPFLOWPrintSolution(w.tcopf, isol); + ExaGOCheckError(ierr); + }) + .def("save_solution", + [](TCOPFLOW_wrapper &w, int n, OutputFormat fmt, + std::string outfile) { + PetscErrorCode ierr; + ierr = TCOPFLOWSaveSolution(w.tcopf, n, fmt, outfile.c_str()); + ExaGOCheckError(ierr); + }) + .def("save_solution_all", + [](TCOPFLOW_wrapper &w, OutputFormat fmt, std::string outdir) { + PetscErrorCode ierr; + ierr = TCOPFLOWSaveSolutionAll(w.tcopf, fmt, outdir.c_str()); + ExaGOCheckError(ierr); + }); +} diff --git a/interfaces/python/exago_python_tcopflow.hpp b/interfaces/python/exago_python_tcopflow.hpp new file mode 100644 index 000000000..77ae084f1 --- /dev/null +++ b/interfaces/python/exago_python_tcopflow.hpp @@ -0,0 +1,8 @@ +#ifndef _exago_python_tcopflow_hpp_ +#define _exago_python_tcopflow_hpp_ + +#include "exago_python_opflow.hpp" +#include +#include + +#endif diff --git a/interfaces/python/test_opflow.py b/interfaces/python/example_opflow.py similarity index 100% rename from interfaces/python/test_opflow.py rename to interfaces/python/example_opflow.py diff --git a/interfaces/python/test_pflow.py b/interfaces/python/example_pflow.py similarity index 100% rename from interfaces/python/test_pflow.py rename to interfaces/python/example_pflow.py diff --git a/interfaces/python/test_scopflow.py b/interfaces/python/example_scopflow.py similarity index 100% rename from interfaces/python/test_scopflow.py rename to interfaces/python/example_scopflow.py diff --git a/interfaces/python/test_sopflow.py b/interfaces/python/example_sopflow.py similarity index 100% rename from interfaces/python/test_sopflow.py rename to interfaces/python/example_sopflow.py diff --git a/interfaces/python/example_tcopflow.py b/interfaces/python/example_tcopflow.py new file mode 100644 index 000000000..5dd631850 --- /dev/null +++ b/interfaces/python/example_tcopflow.py @@ -0,0 +1,17 @@ +import os +import exago + +# Initialize a given application to be run +exago.initialize("tcopflow_test") +tcopf = exago.TCOPFLOW() +path = exago.prefix() +tcopf.set_network_data( + os.path.join(path, 'share', 'exago', 'datafiles', 'case9', 'case9mod.m')) +tcopf.set_solver("IPOPT") +tcopf.solve() +tcopf.print_solution(0) + +# Delete instance before finalization (try, at least) +del tcopf + +exago.finalize() diff --git a/tests/interfaces/python/CMakeLists.txt b/tests/interfaces/python/CMakeLists.txt index 6c8ad4ce1..18169e7a4 100644 --- a/tests/interfaces/python/CMakeLists.txt +++ b/tests/interfaces/python/CMakeLists.txt @@ -22,7 +22,16 @@ endif() # The SCOPFLOW and SOPFLOW API tests are currently written only for IPOPT if(EXAGO_ENABLE_IPOPT) - string(APPEND TEST_FILES " " "test_3_scopflow.py" " " "test_4_sopflow.py") + string( + APPEND + TEST_FILES + " " + "test_3_scopflow.py" + " " + "test_4_sopflow.py" + " " + "test_5_tcopflow.py" + ) endif() string(APPEND TEST_FILES " " "test_9_finalize.py") diff --git a/tests/interfaces/python/test_0_initialize.py b/tests/interfaces/python/test_0_initialize.py index 9fb49ad13..78aa0b685 100644 --- a/tests/interfaces/python/test_0_initialize.py +++ b/tests/interfaces/python/test_0_initialize.py @@ -1,14 +1,12 @@ # test initializing exago only once +from check_preconditions import check_preconditions import os import pytest -from check_preconditions import check_preconditions import mpi4py.rc mpi4py.rc.threads = False from mpi4py import MPI # noqa -import exago # noqa - - check_preconditions() +import exago # noqa @pytest.mark.nocomm diff --git a/tests/interfaces/python/test_1_pflow.py b/tests/interfaces/python/test_1_pflow.py index 829e1e823..513d2232f 100644 --- a/tests/interfaces/python/test_1_pflow.py +++ b/tests/interfaces/python/test_1_pflow.py @@ -1,12 +1,11 @@ +from check_preconditions import check_preconditions import os import pytest -from check_preconditions import check_preconditions import mpi4py.rc mpi4py.rc.threads = False from mpi4py import MPI # noqa -import exago # noqa - check_preconditions() +import exago # noqa @pytest.mark.nocomm diff --git a/tests/interfaces/python/test_2_opflow.py b/tests/interfaces/python/test_2_opflow.py index 1c2ab9aee..acb443a52 100644 --- a/tests/interfaces/python/test_2_opflow.py +++ b/tests/interfaces/python/test_2_opflow.py @@ -1,12 +1,10 @@ +from check_preconditions import check_preconditions import os import pytest -from check_preconditions import check_preconditions import mpi4py.rc mpi4py.rc.threads = False -from mpi4py import MPI # noqa -import exago # noqa - check_preconditions() +import exago # noqa exago_ignore = -1000000 diff --git a/tests/interfaces/python/test_3_scopflow.py b/tests/interfaces/python/test_3_scopflow.py index cbf3811c5..6484e8631 100644 --- a/tests/interfaces/python/test_3_scopflow.py +++ b/tests/interfaces/python/test_3_scopflow.py @@ -1,14 +1,14 @@ +from check_preconditions import check_preconditions import tempfile import os import shutil import pytest -from check_preconditions import check_preconditions import mpi4py.rc mpi4py.rc.threads = False from mpi4py import MPI # noqa +check_preconditions() import exago # noqa -check_preconditions() exago_ignore = -1000000 diff --git a/tests/interfaces/python/test_4_sopflow.py b/tests/interfaces/python/test_4_sopflow.py index adea974d3..a5d7c0ef6 100644 --- a/tests/interfaces/python/test_4_sopflow.py +++ b/tests/interfaces/python/test_4_sopflow.py @@ -1,14 +1,14 @@ +from check_preconditions import check_preconditions import tempfile import os import shutil import pytest -from check_preconditions import check_preconditions import mpi4py.rc mpi4py.rc.threads = False from mpi4py import MPI # noqa +check_preconditions() import exago # noqa -check_preconditions() exago_ignore = -1000000 diff --git a/tests/interfaces/python/test_5_tcopflow.py b/tests/interfaces/python/test_5_tcopflow.py new file mode 100644 index 000000000..d46ec2d61 --- /dev/null +++ b/tests/interfaces/python/test_5_tcopflow.py @@ -0,0 +1,37 @@ +from check_preconditions import check_preconditions +import tempfile +import os +import shutil +import pytest +import mpi4py.rc +mpi4py.rc.threads = False +from mpi4py import MPI # noqa +check_preconditions() +import exago # noqa + + +def run_tcopflow(solver): + tcopf = exago.TCOPFLOW() + path = exago.prefix() + + tcopf.set_tolerance(1.0E-03) + tcopf.set_network_data(os.path.join( + path, 'share', 'exago', 'datafiles', 'case9', 'case9mod_gen3_wind.m')) + tcopf.set_solver(solver) + + tcopf.setup() + tcopf.solve() + + assert tcopf.get_convergence_status() + + obj = tcopf.get_objective() + assert isinstance(obj, float) + + n = tcopf.get_num_iterations() + assert isinstance(n, int) + + +@pytest.mark.nocomm +@pytest.mark.MPI +def test_tcopflow_1(): + run_tcopflow('IPOPT') diff --git a/tests/interfaces/python/test_9_finalize.py b/tests/interfaces/python/test_9_finalize.py index 0fd4229fe..d03a427ea 100644 --- a/tests/interfaces/python/test_9_finalize.py +++ b/tests/interfaces/python/test_9_finalize.py @@ -1,9 +1,8 @@ # test only run once things here import pytest -import exago from check_preconditions import check_preconditions - check_preconditions() +import exago # noqa @pytest.mark.nocomm