Conversation
TimothyWillard
left a comment
There was a problem hiding this comment.
It would nice to see a bit more DRY, especially for low hanging fruit like using types from flepimop2 or helpers already defined in op_engine.
| dt_min: float = Field(default=0.0, ge=0.0) | ||
| dt_max: float = Field(default=float("inf"), gt=0.0) |
There was a problem hiding this comment.
Does dt_min == 0.0 mean infer lower bound and dt_max == math.inf mean infer upper bound? Minor, but seems like a more natural choice would be None since that indicates no user statement.
There was a problem hiding this comment.
I kept dt_min=0.0 and dt_max=inf intentionally because those are the native op_engine DtControllerConfig sentinel values for ‘no additional bound’. Switching to None in the provider would require extra normalization logic and would diverge from core config semantics without changing behavior. If we want None ergonomics, we should introduce it consistently in op_engine itself first, then mirror it here.
There was a problem hiding this comment.
Yeah, that seems reasonable to mirror the API of op_engine here. I guess just noting its a bit weird to use concrete values rather than values that indicate "no statement", but I guess this is outside the scope of this PR then.
There was a problem hiding this comment.
This is not a TODO for you @MacdonaldJoshuaCaleb instead for me, but probably need to provide some kind of default system/engine as part of the integration testing utilities bundled with flepimop2.
pearsonca
left a comment
There was a problem hiding this comment.
Minor questions, but seems fine?
| return None | ||
|
|
||
|
|
||
| def _rhs_from_stepper( |
There was a problem hiding this comment.
work for another day: if adapter is necessary, we should probably be working the pipeline definition a bit. i imagine generally engines may need to adapt SystemABC somewhat, but all this re-shaping seems problematic.
There was a problem hiding this comment.
This actually seems fine to me, and within the scope of engines. This is taking a stepper function and then vectorizing it because that's the type of "stepper" that op_engine expects. I don't think flepimop2 should get into the business of trying to figure out the correct way(s) to generalize steppers. However, I do think this could be simplified a bit by the introduction of proper "build" mechanics, see ACCIDDA/flepimop2#130, ACCIDDA/flepimop2#129 (comment).
There was a problem hiding this comment.
I'm mostly bothered by the shaping related checks than the reshaping itself. I should have said if this amount of adaptor required
There was a problem hiding this comment.
I see, I think this is a consequence of the outputs being unstructured numpy arrays. To address this would require adding more structure on that front. Perhaps ACCIDDA/flepimop2#147 is a first step to that, but definitely more work required beyond that.
MacdonaldJoshuaCaleb
left a comment
There was a problem hiding this comment.
Why not use
op_engine.types.as_float_1d?
as part of the simplifications I got rid of the types.py file
| from flepimop2.exceptions import ValidationIssue | ||
| from flepimop2.system.abc import SystemABC, SystemProtocol | ||
| from pydantic import BaseModel, ConfigDict, Field, model_validator | ||
| from flepimop2.typing import StateChangeEnum # noqa: TC002 |
There was a problem hiding this comment.
Instead of noqaing, why not move this under the type checking imports as the lint rule suggests? See https://docs.astral.sh/ruff/rules/typing-only-third-party-import/.
| return None | ||
|
|
||
|
|
||
| def _rhs_from_stepper( |
There was a problem hiding this comment.
This actually seems fine to me, and within the scope of engines. This is taking a stepper function and then vectorizing it because that's the type of "stepper" that op_engine expects. I don't think flepimop2 should get into the business of trying to figure out the correct way(s) to generalize steppers. However, I do think this could be simplified a bit by the introduction of proper "build" mechanics, see ACCIDDA/flepimop2#130, ACCIDDA/flepimop2#129 (comment).
| dt_min: float = Field(default=0.0, ge=0.0) | ||
| dt_max: float = Field(default=float("inf"), gt=0.0) |
There was a problem hiding this comment.
Yeah, that seems reasonable to mirror the API of op_engine here. I guess just noting its a bit weird to use concrete values rather than values that indicate "no statement", but I guess this is outside the scope of this PR then.
| """flepimop2 engine adapter backed by op_engine.CoreSolver.""" | ||
|
|
||
| module: Literal["flepimop2.engine.op_engine"] = "flepimop2.engine.op_engine" | ||
| state_change: StateChangeEnum |
There was a problem hiding this comment.
Okay, I'm a bit confused about this one. It seems like this seems to indicate that op_engine handles multiple different kinds of state change steppers, but requires user configuration to do so? If op_engine can handle multiple kinds of state change steppers it should just use system.state_change to infer the settings for op_engine. However, it appears like a validation is done, and then self.state_change is never used? So can op_engine actually handle steppers of different state changes, from your description of the package I thought it could only handle "flow"?
| if isinstance(system_axis, str | int): | ||
| operator_axis = system_axis | ||
|
|
||
| stepper: SystemProtocol = system._stepper # noqa: SLF001 |
There was a problem hiding this comment.
Another argument for pushing ACCIDDA/flepimop2#130 forward in the workplan
| tr=operators.get("tr"), | ||
| bdf2=operators.get("bdf2"), | ||
| ) | ||
| __all__ = ["OpEngineEngineConfig", "_coerce_operator_specs", "_has_operator_specs"] |
There was a problem hiding this comment.
Why export _coerce_operator_specs & _has_operator_specs? These seem like private utilities.
|
|
||
| [tool.ruff.lint.per-file-ignores] | ||
| "tests/**/*" = ["INP001", "S101"] | ||
| "src/flepimop2/engine/op_engine/__init__.py" = ["C901", "DOC201", "DOC501", "PLR0912", "PLR0913", "PLR0914", "PLR0915", "RUF067"] |
There was a problem hiding this comment.
This makes me a bit weary to ignore so many linting/formatting suggestions. Perhaps outside the scope of this PR, but I think these rules exist to indicate where there might be refactoring/documentation opportunities. Although, for RUF067 we also need to think about that on the flepimop2 front, the way we've structured the package as being an implicit namespace package with dynamic module loading makes it a little annoying to work around this one.
This pull request consolidates and refactors the integration between
flepimop2andop_engine, moving all engine logic and configuration into a single file (__init__.py). It removes the separateconfig.pyandengine.pymodules, simplifies dependency management inpyproject.toml, and updates linting rules. The new implementation provides a streamlined, self-contained flepimop2 engine backed by op_engine, with improved configuration validation and runtime checks.Engine integration and refactoring:
src/flepimop2/engine/op_engine/__init__.py, replacing the previous split betweenengine.pyandconfig.py. The new implementation defines theOpEngineFlepimop2Engineand its configuration in a single file, with improved runtime validation and operator handling.Dependency and build configuration:
Updated
pyproject.tomlto referenceop-enginedirectly from its GitHub repository, removing the sibling directory wiring and simplifying dependency management.Linting and code quality:
Added per-file ignore rules in
pyproject.tomlfor the new consolidated__init__.py, disabling specific lint and docstyle checks to accommodate the new implementation.Aligned provider package with the expectations setup in flepimop2 #118
Closes #6 obsoletes #10, #11 due to changes introduced in flepimop2 #118