Skip to content

Add compas_3dec solver integration - #23

Merged
tomvanmele merged 8 commits into
BlockResearchGroup:mainfrom
adellendice:3dec-integration-refactor
Aug 28, 2026
Merged

Add compas_3dec solver integration#23
tomvanmele merged 8 commits into
BlockResearchGroup:mainfrom
adellendice:3dec-integration-refactor

Conversation

@adellendice

Copy link
Copy Markdown
Contributor

Summary

Integrate compas_3dec as a solver backend for compas_dem.

Changes

  • Add Solver.ThreeDEC(...) configuration.
  • Dispatch Problem.solve() to the ThreeDEC adapter.
  • Convert a compas_dem.Problem with
    ThreeDECAnalysisBuilder.from_dem_problem(problem).build().
  • Run the prepared analysis with ThreeDECSolver.
  • Convert native 3DEC output through
    raw_results.to_compas_dem_results(analysis).
  • Add solver configuration and dispatch tests.
  • Add an opt-in real 3DEC gravity smoke test.
  • Add a manual arch-gravity example with DEMViewer visualisation.

Verification

  • Ruff formatting and linting pass.
  • Automated adapter tests pass.
  • Real 3DEC arch gravity analysis completes successfully.
  • Converted DEM results display successfully in DEMViewer.

Notes

compas_3dec remains an optional dependency. The real solver smoke test is
skipped unless COMPAS_3DEC_EXECUTABLE is configured.

Comment thread src/compas_dem/problem/problem.py Outdated
>>> problem = Problem(model)
>>> problem.add_gravity()
>>> problem.solver(Solver.CRA()) # doctest: +SKIP
>>> _ = problem.add_boundary_condition("gravity").add_gravity()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you add # doctest: +SKIP the awkward _ assignment is not necessary

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please address

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the _ assignment.

Comment thread requirements-dev.txt
@adellendice

adellendice commented Aug 25, 2026

Copy link
Copy Markdown
Contributor Author

@tomvanmele I added one follow-up commit after the initial approval:

  • configurable 3DEC execution stages;
  • forwarding of progress callbacks and UI event pumping;
  • regression tests for the new runtime options.

All checks pass locally: Ruff passed and pytest reported 7 passed, 1 skipped.

Could you please briefly re-review the latest commit before merging?

@tomvanmele tomvanmele left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i recognize typical LLM interventions...

Comment thread src/compas_dem/analysis/threedec.py Outdated

# These values live in Problem.solver and are picked up by
# ThreeDECAnalysisBuilder when it creates the portable snapshot.
_ = (ratio, ratio_keyword, time, gravity_steps, stages)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the dummy assignment and now pass the ratio, ratio keyword, time, gravity steps and optional stages explicitly to the prepared 3DEC analysis.

Comment thread src/compas_dem/elements/block.py Outdated
@@ -150,4 +150,3 @@ def compute_obb(self, inflate: float = 1.0) -> Box:

def compute_point(self) -> Point:
return Point(*self.modelgeometry.centroid())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think it is necessary convert. return of centroid() is already a point object

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, changed. Now, it is not constructing another Point.

--------
>>> bc = BoundaryConditionGroup(name="Live")
>>> bc.add_point_load(block_index=10, force=[0, 0, -5000])
>>> _ = bc.add_point_load(block_index=10, force=[0, 0, -5000])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't like this kind of thing. i assume it is to avoid a doctest error. if there is a meaningful return object, just assign it to a meaningful variable...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. The returned point load is now assigned to a point_load variable and used in the example.

Comment thread src/compas_dem/problem/problem.py Outdated
>>> problem = Problem(model)
>>> problem.add_gravity()
>>> problem.solver(Solver.CRA()) # doctest: +SKIP
>>> _ = problem.add_boundary_condition("gravity").add_gravity()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please address

Comment thread src/compas_dem/problem/problem.py Outdated
from compas_dem.analysis.lmgc90 import lmgc90_solve

return lmgc90_solve(self, model, **params)
elif solver.name in ("3DEC", "threeDEC"):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when using returns in your ifs, elifs are not necessary...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, changed.

Comment thread src/compas_dem/problem/solvers.py Outdated
return self

@staticmethod
def _validate_threedec_stages(stages):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static methods that are not part of a public API (which would be a valid reason to add it to the class even though it doesn't interact with it), should not be methods, but rather private module scope functions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved the stage-validation function to module level.

@adellendice

Copy link
Copy Markdown
Contributor Author

@tomvanmele please review. Thanks

@tomvanmele tomvanmele left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@tomvanmele
tomvanmele merged commit 2bf626c into BlockResearchGroup:main Aug 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants