Orrery
A GPU-accelerated N-body gravitational simulator
Loading...
Searching...
No Matches
orrery::solvers::DirectSolver Class Referencefinal

The O(N^2) solver described above. More...

#include <orrery/solvers/direct_solver.hpp>

Inheritance diagram for orrery::solvers::DirectSolver:
orrery::solvers::ForceSolver orrery::integrators::AccelerationField

Public Member Functions

 DirectSolver ()=default
 A solver over exact point masses, evaluated on the calling thread.
 DirectSolver (core::Softening softening) noexcept
 A solver softening at the given length.
 DirectSolver (backend::Executor &executor) noexcept
 A solver over exact point masses, evaluated through executor.
 DirectSolver (core::Softening softening, backend::Executor &executor) noexcept
 A softened solver evaluated through executor.
void evaluate (core::Vec3Span< const core::Real > positions, std::span< const core::Real > masses, core::Vec3Span< core::Real > accelerations) override
 Write the acceleration at each position into accelerations.
std::string_view name () const noexcept override
 The solver's name, for benchmark tables and test messages.
core::Softening softening () const noexcept override
 The softening this solver applies.
InteractionCount interaction_count () const noexcept override
 The work done since construction or since the last reset.
void reset_interaction_count () noexcept override
 Set every counter back to zero.
const backend::Executorexecutor () const noexcept
 The executor this solver evaluates through, or null if it runs on the calling thread.
void select_kernel (KernelKind kind) noexcept
 Ask for a particular kernel, and settle for the scalar one if this machine cannot run it.
KernelKind kernel () const noexcept
 The kernel this solver will actually use.
Public Member Functions inherited from orrery::solvers::ForceSolver
 ~ForceSolver () override=default
 Public and virtual, the second by inheritance from AccelerationField.

Additional Inherited Members

Protected Member Functions inherited from orrery::solvers::ForceSolver
 ForceSolver (const ForceSolver &)=default
 ForceSolver (ForceSolver &&)=default
ForceSolver & operator= (const ForceSolver &)=default
ForceSolver & operator= (ForceSolver &&)=default
Protected Member Functions inherited from orrery::integrators::AccelerationField
 AccelerationField (const AccelerationField &)=default
 AccelerationField (AccelerationField &&)=default
AccelerationField & operator= (const AccelerationField &)=default
AccelerationField & operator= (AccelerationField &&)=default

Detailed Description

The O(N^2) solver described above.

Constructor & Destructor Documentation

◆ DirectSolver() [1/2]

orrery::solvers::DirectSolver::DirectSolver ( core::Softening softening)
inlineexplicitnoexcept

A solver softening at the given length.

explicit for the reason Softening's own constructor is: the argument is the modelling decision, not a spelling of the solver.

◆ DirectSolver() [2/2]

orrery::solvers::DirectSolver::DirectSolver ( core::Softening softening,
backend::Executor & executor )
inlinenoexcept

A softened solver evaluated through executor.

The executor is referred to rather than owned, and must outlive the solver. That is the right ownership for what it is: a pool of threads is a machine-wide resource, one is enough for a whole simulation, and a solver that owned one could not be created inside a loop without creating and destroying eight threads each time round.

Member Function Documentation

◆ evaluate()

void orrery::solvers::DirectSolver::evaluate ( core::Vec3Span< const core::Real > positions,
std::span< const core::Real > masses,
core::Vec3Span< core::Real > accelerations )
overridevirtual

Write the acceleration at each position into accelerations.

The three views must describe the same particles in the same order and have the same length, as AccelerationField requires. That precondition is not checked here: the check would have to throw or terminate, and no exception may leave a kernel, so it belongs at the boundary where the spans are formed rather than in the loop that consumes them.

Safe to call with no particles, which is what an empty configuration and a partially built one both look like.

Implements orrery::integrators::AccelerationField.

◆ name()

std::string_view orrery::solvers::DirectSolver::name ( ) const
inlinenodiscardoverridevirtualnoexcept

The solver's name, for benchmark tables and test messages.

Implements orrery::solvers::ForceSolver.

◆ softening()

core::Softening orrery::solvers::DirectSolver::softening ( ) const
inlinenodiscardoverridevirtualnoexcept

The softening this solver applies.

Exposed so that a caller measuring energies asks the solver what it softened with rather than carrying a second copy of the value alongside it. Two copies of a number that must agree are how they come to disagree, and a potential energy computed with a different softening from the force kernel turns the project's headline conservation result into a measurement of the mismatch (ADR-0008).

Implements orrery::solvers::ForceSolver.

◆ interaction_count()

InteractionCount orrery::solvers::DirectSolver::interaction_count ( ) const
inlinenodiscardoverridevirtualnoexcept

The work done since construction or since the last reset.

Cheap: solvers accumulate this as they go and this call only reads it.

Implements orrery::solvers::ForceSolver.

◆ reset_interaction_count()

void orrery::solvers::DirectSolver::reset_interaction_count ( )
inlineoverridevirtualnoexcept

Set every counter back to zero.

A benchmark counts the work of the measured region rather than of the warm-up that preceded it, and the two are otherwise indistinguishable once they have been added together.

Implements orrery::solvers::ForceSolver.

◆ executor()

const backend::Executor * orrery::solvers::DirectSolver::executor ( ) const
inlinenodiscardnoexcept

The executor this solver evaluates through, or null if it runs on the calling thread.

Exposed so that a benchmark can report which scheme produced a timing alongside the timing itself, which is the one thing a threading figure is useless without.

◆ select_kernel()

void orrery::solvers::DirectSolver::select_kernel ( KernelKind kind)
inlinenoexcept

Ask for a particular kernel, and settle for the scalar one if this machine cannot run it.

Deliberately not a constructor argument. A simulation should take the fastest kernel available and never mention the subject; the callers that care are the accuracy test comparing two kernels on one configuration and the benchmark timing them against each other, and both of those want to change the choice on a solver they already hold rather than to build a second one.

Silently falling back rather than refusing is the right behaviour for the same reason accumulate_range_for never returns null: a request for AVX2 on a machine without it should produce correct physics, and the caller that cares which kernel ran asks kernel().


The documentation for this class was generated from the following file: