Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
bcedee0
CLI experiment with singed commit
TonyXiang8787 Jul 1, 2026
1b0d2f6
Merge branch 'main' into feature/pgm-cli
mgovers Jul 9, 2026
ce329a6
down merge
TonyXiang8787 Jul 17, 2026
9f0e593
Merge branch 'main' into feature/pgm-cli
TonyXiang8787 Jul 20, 2026
63b2c0e
Merge branch 'main' into feature/pgm-cli
TonyXiang8787 Jul 21, 2026
13e1f18
Remove GitHub release job from workflow
TonyXiang8787 Jul 21, 2026
3b2548f
help cli
TonyXiang8787 Jul 22, 2026
d9b2015
try sign
TonyXiang8787 Jul 22, 2026
10ae34f
Merge branch 'main' into feature/pgm-cli
TonyXiang8787 Jul 22, 2026
aaa70c0
try to split
TonyXiang8787 Jul 22, 2026
417b17b
reuse python shim
TonyXiang8787 Jul 22, 2026
fc99c28
add mutex
TonyXiang8787 Jul 22, 2026
532316a
call back
TonyXiang8787 Jul 22, 2026
21fd9c3
message call back
TonyXiang8787 Jul 22, 2026
bccf944
get cli test
TonyXiang8787 Jul 22, 2026
0e090a4
cli test
TonyXiang8787 Jul 22, 2026
dd4c74a
test more
TonyXiang8787 Jul 22, 2026
b919bac
use c
TonyXiang8787 Jul 22, 2026
42eb6ce
test shim
TonyXiang8787 Jul 22, 2026
0899a7d
Update test target file path for PGMCLI
TonyXiang8787 Jul 23, 2026
d282788
Fix CLI linker language for GCC sanitizer builds
Copilot Jul 23, 2026
b8be263
sonar
TonyXiang8787 Jul 23, 2026
534bc34
clang tidy
TonyXiang8787 Jul 23, 2026
45c9338
Merge branch 'main' into feature/pgm-cli
TonyXiang8787 Jul 24, 2026
72a8be0
cmake
TonyXiang8787 Jul 26, 2026
f6215fe
cpp cli
TonyXiang8787 Jul 26, 2026
3c26909
cli
TonyXiang8787 Jul 26, 2026
5767dce
Merge branch 'main' into feature/pgm-cli
TonyXiang8787 Jul 27, 2026
b763826
use ranges transform
TonyXiang8787 Jul 27, 2026
f35a02e
test more
TonyXiang8787 Jul 27, 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
3 changes: 2 additions & 1 deletion .github/conda_pgm_env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
name: conda-pgm-env
dependencies:
# build env
- python=3.12
- python=3.14
- pip
- scikit-build-core
# build deps
Expand All @@ -16,6 +16,7 @@ dependencies:
- numpy
- cmake
- ninja
- cli11
# test deps
- pytest
- pytest-cov
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/build-test-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,12 @@ jobs:
cmake --install build/

- name: Build python
run: python -m pip install . --no-build-isolation --no-deps -C wheel.cmake=false
run: |
python conda_build_preparation.py
python -m pip install . --no-build-isolation --no-deps -C wheel.cmake=false

- name: Test
run: pytest
run: |
pytest
power-grid-model --help
power-grid-model --version
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ find_package(Boost REQUIRED)
find_package(Eigen3 CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
find_package(msgpack-cxx REQUIRED)
find_package(CLI11 CONFIG REQUIRED)
Comment thread
TonyXiang8787 marked this conversation as resolved.

if(NOT WIN32)
# thread
Expand Down
31 changes: 31 additions & 0 deletions conda_build_preparation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0

# This script modifies the pyproject.toml file to remove specific sections
# [project.entry-points."cmake.root"] and [project.scripts]
# and deletes the run_pgm_cli.py file from the source directory.
# It is intended to be run as part of the conda build preparation process.
# So that conda environment will not be confused with PyPI style Python shim and entry points.

import re
from pathlib import Path

# Read the root pyproject.toml
pyproject_path = Path(__file__).parent / "pyproject.toml"
content = pyproject_path.read_text()

# Remove [project.entry-points."cmake.root"] section
content = re.sub(r'\n\[project\.entry-points\."cmake\.root"\].*?(?=\n\[|\Z)', "", content, flags=re.DOTALL)

# Remove [project.scripts] section
content = re.sub(r"\n\[project\.scripts\].*?(?=\n\[|\Z)", "", content, flags=re.DOTALL)

# Write back to pyproject.toml
pyproject_path.write_text(content)

# Remove run_pgm_cli.py file
run_pgm_cli_path = (
Path(__file__).parent / "src" / "power_grid_model" / "_core" / "power_grid_model_c" / "run_pgm_cli.py"
)
run_pgm_cli_path.unlink(missing_ok=True)
2 changes: 2 additions & 0 deletions power_grid_model_c/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@
add_subdirectory("power_grid_model")
add_subdirectory("power_grid_model_c")
add_subdirectory("power_grid_model_cpp")
add_subdirectory("power_grid_model_cli_backend")
add_subdirectory("power_grid_model_cli")
Comment thread
TonyXiang8787 marked this conversation as resolved.
21 changes: 21 additions & 0 deletions power_grid_model_c/power_grid_model_cli/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0

add_executable(power_grid_model_cli main.cpp)

target_link_libraries(power_grid_model_cli PRIVATE power_grid_model_cli_backend)

set_property(TARGET power_grid_model_cli PROPERTY INSTALL_RPATH_USE_LINK_PATH FALSE)
set_property(TARGET power_grid_model_cli PROPERTY OUTPUT_NAME "power-grid-model")
if(APPLE)
set_property(TARGET power_grid_model_cli PROPERTY INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}")
elseif(UNIX) # Linux, BSD (not Windows/macOS)
set_property(TARGET power_grid_model_cli PROPERTY INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}")
endif()

install(
TARGETS power_grid_model_cli
EXPORT power_grid_modelTargets
COMPONENT power_grid_model
)
7 changes: 7 additions & 0 deletions power_grid_model_c/power_grid_model_cli/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
//
// SPDX-License-Identifier: MPL-2.0

#include <power_grid_model_cli_backend/cli_main.h>

int main(int argc, char** argv) { return PGM_cli_main(argc, argv, nullptr, nullptr, nullptr); }
73 changes: 73 additions & 0 deletions power_grid_model_c/power_grid_model_cli_backend/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
#
# SPDX-License-Identifier: MPL-2.0

add_library(
power_grid_model_cli_backend
SHARED
"src/cli_main.cpp"
"src/cli_options.cpp"
"src/pgm_calculation.cpp"
)

target_include_directories(
power_grid_model_cli_backend
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/src
)

file(
GLOB_RECURSE pgm_cli_backend_public_headers
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.h"
)

target_link_libraries(power_grid_model_cli_backend PRIVATE power_grid_model_c)
target_link_libraries(power_grid_model_cli_backend PRIVATE power_grid_model_cpp)
target_link_libraries(power_grid_model_cli_backend PRIVATE CLI11::CLI11)

target_compile_definitions(
power_grid_model_cli_backend
PRIVATE
PGM_CLI_BACKEND_EXPORTS
PGM_ENABLE_EXPERIMENTAL
)

target_sources(
power_grid_model_cli_backend
PUBLIC
FILE_SET pgm_cli_backend_public_headers
TYPE HEADERS
BASE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include/"
FILES "${pgm_cli_backend_public_headers}"
)

set_target_properties(
power_grid_model_cli_backend
PROPERTIES VERSION ${PGM_VERSION} SOVERSION ${PGM_VERSION_MAJOR}
)

set_property(TARGET power_grid_model_cli_backend PROPERTY INSTALL_RPATH_USE_LINK_PATH FALSE)
if(APPLE)
set_property(TARGET power_grid_model_cli_backend PROPERTY INSTALL_RPATH "@loader_path")
elseif(UNIX) # Linux, BSD (not Windows/macOS)
set_property(TARGET power_grid_model_cli_backend PROPERTY INSTALL_RPATH "$ORIGIN")
endif()

if(NOT PGM_MSVC_REPRODUCIBLE_BUILD)
set_target_properties(
power_grid_model_cli_backend
PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE
)
endif()

install(
TARGETS power_grid_model_cli_backend
EXPORT power_grid_modelTargets
COMPONENT power_grid_model
FILE_SET pgm_cli_backend_public_headers
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
//
// SPDX-License-Identifier: MPL-2.0

#pragma once
#ifndef POWER_GRID_MODEL_CLI_BACKEND_CLI_MAIN_H
#define POWER_GRID_MODEL_CLI_BACKEND_CLI_MAIN_H

#ifdef _WIN32
#define PGM_CLI_HELPER_DLL_IMPORT __declspec(dllimport)
#define PGM_CLI_HELPER_DLL_EXPORT __declspec(dllexport)
#define PGM_CLI_HELPER_DLL_LOCAL
#else
#if __GNUC__ >= 4
#define PGM_CLI_HELPER_DLL_IMPORT __attribute__((visibility("default")))
#define PGM_CLI_HELPER_DLL_EXPORT __attribute__((visibility("default")))
#define PGM_CLI_HELPER_DLL_LOCAL __attribute__((visibility("hidden")))
#else
#define PGM_CLI_HELPER_DLL_IMPORT
#define PGM_CLI_HELPER_DLL_EXPORT
#define PGM_CLI_HELPER_DLL_LOCAL
#endif
#endif

#ifdef PGM_CLI_BACKEND_EXPORTS
#define PGM_CLI_API PGM_CLI_HELPER_DLL_EXPORT
#else
#define PGM_CLI_API PGM_CLI_HELPER_DLL_IMPORT
#endif
#define PGM_CLI_LOCAL PGM_CLI_HELPER_DLL_LOCAL

#ifdef __cplusplus
#define PGM_CLI_NOEXCEPT noexcept
#else
#define PGM_CLI_NOEXCEPT
#endif

#ifdef __cplusplus
extern "C" {
#endif

// NOLINTNEXTLINE(modernize-use-using)
typedef void (*PGM_CLIMessageCallback)(char const* msg, void* user_data);

/**
* @brief CLI entry point for the Power Grid Model command line interface.
*
* @param argc Number of command line arguments.
* @param argv Command line arguments.
* @param cout_callback Callback used for standard output. When null, std::cout is used.
* @param cerr_callback Callback used for standard error. When null, std::cerr is used.
* @param user_data Opaque user data forwarded to the callbacks.
* @return Process exit code.
*/
PGM_CLI_API int PGM_cli_main(int argc, char** argv, PGM_CLIMessageCallback cout_callback,
PGM_CLIMessageCallback cerr_callback, void* user_data) PGM_CLI_NOEXCEPT;

#ifdef __cplusplus
}
#endif

#endif // POWER_GRID_MODEL_CLI_BACKEND_CLI_MAIN_H
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
//
// SPDX-License-Identifier: MPL-2.0

#pragma once

#include <power_grid_model_cpp.hpp>

#include <filesystem>
#include <map>
#include <ostream>
#include <set>
#include <vector>

namespace power_grid_model_cpp {

struct CLIResult {
int exit_code;
bool should_exit;
std::string stdout_message;
std::string stderr_message;

operator bool() const { return should_exit || exit_code != 0; }
};

// NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding)
struct ClIOptions { // NOSONAR(S1820)
std::filesystem::path input_file;
std::vector<std::filesystem::path> batch_update_file;
std::filesystem::path output_file;
PGM_SerializationFormat input_serialization_format{PGM_json};
std::vector<PGM_SerializationFormat> batch_update_serialization_format;
bool is_batch{false};

double system_frequency{50.0};

PGM_CalculationType calculation_type{PGM_power_flow};
PGM_CalculationMethod calculation_method{PGM_default_method};
bool symmetric_calculation{static_cast<bool>(PGM_symmetric)};
double error_tolerance{1e-8};
Idx max_iterations{20};
Idx threading{-1};
PGM_ShortCircuitVoltageScaling short_circuit_voltage_scaling{PGM_short_circuit_voltage_scaling_maximum};
PGM_TapChangingStrategy tap_changing_strategy{PGM_tap_changing_strategy_disabled};

bool use_msgpack_output_serialization{false};
Idx output_json_indent{2};
bool use_compact_serialization{false};

MetaDataset const* output_dataset{nullptr};
std::map<MetaComponent const*, std::set<MetaAttribute const*>> output_component_attribute_filters;

bool verbose{false};

friend std::ostream& operator<<(std::ostream& os, ClIOptions const& options);
};

CLIResult parse_cli_options(int argc, char** argv, ClIOptions& options);

void pgm_calculation(ClIOptions const& cli_options);

} // namespace power_grid_model_cpp
66 changes: 66 additions & 0 deletions power_grid_model_c/power_grid_model_cli_backend/src/cli_main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Contributors to the Power Grid Model project <powergridmodel@lfenergy.org>
//
// SPDX-License-Identifier: MPL-2.0

#include "power_grid_model_cli_backend/cli_main.h"

#include "cli_functions.hpp"

#include <power_grid_model_cpp/handle.hpp>

#include <exception>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>

using namespace power_grid_model_cpp;

namespace {

void write_output(PGM_CLIMessageCallback callback, // NOSONAR(S5205)
void* user_data, // NOSONAR(S5008)
std::ostream& stream, std::string const& message) {
if (callback != nullptr) {
callback(message.c_str(), user_data);
} else {
stream << message;
}
}

} // namespace

int PGM_cli_main(int argc, char** argv, // CLI argument
PGM_CLIMessageCallback cout_callback, PGM_CLIMessageCallback cerr_callback, // NOSONAR(S5205)
void* user_data) noexcept { // NOSONAR(S5008)
static std::mutex cli_mutex;
std::scoped_lock<std::mutex> const lock{cli_mutex};

ClIOptions cli_options;
if (auto const parse_result = parse_cli_options(argc, argv, cli_options); parse_result) {
write_output(cout_callback, user_data, std::cout, parse_result.stdout_message);
write_output(cerr_callback, user_data, std::cerr, parse_result.stderr_message);
return parse_result.exit_code;
}

if (cli_options.verbose) {
std::ostringstream output_stream;
output_stream << cli_options << '\n';
write_output(cout_callback, user_data, std::cout, output_stream.str());
}

try {
pgm_calculation(cli_options);
} catch (PowerGridError const& e) {
write_output(cerr_callback, user_data, std::cerr, std::string{"PowerGridError: "} + e.what() + '\n');
return static_cast<int>(e.error_code());
} catch (std::exception const& e) {
write_output(cerr_callback, user_data, std::cerr, std::string{"Exception: "} + e.what() + '\n');
return 1;
} catch (...) {
write_output(cerr_callback, user_data, std::cerr, "Unknown exception caught.\n");
return 1;
}

return 0;
}
Loading
Loading