votca 2026-dev
Loading...
Searching...
No Matches
ewaldperiodicdipoleoperator.h
Go to the documentation of this file.
1/*
2 * Copyright 2009-2026 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20#pragma once
21#ifndef VOTCA_XTP_EWALDPERIODICDIPOLEOPERATOR_H
22#define VOTCA_XTP_EWALDPERIODICDIPOLEOPERATOR_H
23
24// Standard includes
25#include <string>
26#include <vector>
27
28// Local VOTCA includes
29#include "eeinteractor.h"
30#include "eigen.h"
31#include "ewaldrealspacesum.h"
33#include "ewaldregistry.h"
35
297
298namespace votca {
299namespace xtp {
300class EwaldPeriodicDipoleOperator;
301}
302} // namespace votca
303
304namespace Eigen {
305namespace internal {
306// EwaldPeriodicDipoleOperator's traits must be specialized before the
307// class itself inherits from Eigen::EigenBase<EwaldPeriodicDipoleOperator>
308// below (Eigen::EigenBase's own definition instantiates
309// internal::traits<Derived> immediately) -- matching the exact ordering
310// DipoleDipoleInteraction's own header (dipoledipoleinteraction.h) uses
311// for the same reason.
312template <>
313struct traits<votca::xtp::EwaldPeriodicDipoleOperator>
314 : public Eigen::internal::traits<Eigen::MatrixXd> {};
315} // namespace internal
316} // namespace Eigen
317
318namespace votca {
319namespace xtp {
320
322 : public Eigen::EigenBase<EwaldPeriodicDipoleOperator> {
323 public:
324 // Required typedefs, constants, and method:
325 using Scalar = double;
326 using RealScalar = double;
328 enum {
329 ColsAtCompileTime = Eigen::Dynamic,
330 MaxColsAtCompileTime = Eigen::Dynamic,
332 };
333
334 // ids: the segments this operator solves for, in the order that
335 // defines the layout of any vector this operator is applied to (see
336 // class documentation for the exact, variable-width layout). Every
337 // id must be registered in registry at EwaldChargeState::Neutral.
338 // alpha_ewald: Ewald splitting parameter, shared with real_sum/
339 // recip_sum's own construction, needed here to build a matching
340 // erfc-screened intramolecular interactor.
341 // thole_a: the SAME Thole damping parameter real_sum's own
342 // construction uses (polarmethod.aDamp in ewdbgpol.xml terms). This
343 // used to be an unused placeholder here (0.39, hardcoded, since
344 // intra_interactor_ was originally only ever used for its own public
345 // ComputeB): AddIntraSegmentCoupling did not apply Thole damping to
346 // the intramolecular term at all, on the -- since corrected --
347 // assumption that legacy's own treatment was undamped there too (see
348 // AddIntraSegmentCoupling's own documentation for the fuller
349 // history). It now genuinely calls ComputeThole, so this parameter
350 // is genuinely used and must be the real value, not a placeholder.
351 // shape: the SAME EwaldShapeCorrection instance EwaldBackground's own
352 // permanent-field generation uses. This class previously had NO
353 // shape-correction mechanism at all for the induced case -- a real,
354 // previously-unnoticed gap, found only once EwdInteractor's own
355 // FU12_ShapeField_At_By was traced directly (called unconditionally
356 // inside PolarBackground's own induction-iteration loop, mirroring
357 // FP12_ShapeField_At_By's own permanent-field treatment exactly, but
358 // using each site's own CURRENT induced dipole rather than its
359 // static one). EwaldShapeCorrection::TotalDipoleMoment already sums
360 // charge + static dipole + induced dipole together (see its own
361 // documentation), so no new shape-correction class or formula is
362 // needed here -- only a new call site, added inside RawMultiply
363 // itself (see there for why this is genuinely linear in v and does
364 // not disturb baseline_'s own established v=0 subtraction pattern).
366 const EwaldRealSpaceSum& real_sum,
367 const EwaldReciprocalSpaceSum& recip_sum,
368 const EwaldShapeCorrection& shape,
369 std::vector<Index> ids, double alpha_ewald,
370 double thole_a);
371
372 // Debug/experimental. Accumulated wall-clock spent inside RawMultiply,
373 // split by phase, across every call for the lifetime of this object.
374 // Added to attribute the per-iteration cost of the solve without a
375 // sampling profiler: the phases below are each a single loop, so a
376 // deterministic accumulator answers "where does the matvec time go"
377 // exactly, where sampling an inlined hot function tends to report
378 // little beyond "in AddFieldAt". Times are in seconds; n_calls is the
379 // number of RawMultiply invocations they are summed over (one per
380 // solver iteration, plus one for the baseline_ construction).
382 double setup = 0.0; // dipole set + Reset over every site
383 double real_space = 0.0; // EwaldRealSpaceSum::AddFieldAt
384 double reciprocal = 0.0; // EwaldReciprocalSpaceSum::AddFieldAtMany
385 double shape = 0.0; // EwaldShapeCorrection::AddFieldAt
386 double assemble = 0.0; // P^-1*v - V - M*v result assembly
387 double intra = 0.0; // AddIntraSegmentCoupling
389 double total() const {
391 }
392 };
393 const RawMultiplyTimings& Timings() const { return timings_; }
394
396 public:
398 : xpr_(xpr), id_(id) {};
399
401 row_++;
402 return *this;
403 }
404 operator bool() const { return row_ < xpr_.size_; }
405 double value() const { return xpr_(row_, id_); }
406 Index row() const { return row_; }
407 Index col() const { return id_; }
408 Index index() const { return row(); }
409
410 private:
412 const Index id_;
414 };
415
416 Index rows() const { return size_; }
417 Index cols() const { return size_; }
418 Index outerSize() const { return size_; }
419
420 template <typename Vtype>
421 Eigen::Product<EwaldPeriodicDipoleOperator, Vtype, Eigen::AliasFreeProduct>
422 operator*(const Eigen::MatrixBase<Vtype>& x) const {
423 return Eigen::Product<EwaldPeriodicDipoleOperator, Vtype,
424 Eigen::AliasFreeProduct>(*this, x.derived());
425 }
426
427 // See class documentation: same-site entries return the real getPInv()
428 // block; cross-site entries return 0.0 unconditionally (never actually
429 // read by DiagonalPreconditioner, and expensive to compute honestly
430 // for a periodic pair).
431 double operator()(Index i, Index j) const;
432
433 // The actual matrix-vector product; see class documentation for what
434 // this computes.
435 Eigen::VectorXd multiply(const Eigen::VectorXd& v) const;
436
437 private:
438 // Computes getPInv()*v + (periodic field from every OTHER registered
439 // segment, including those not in ids_) at every ids_ site. Not itself
440 // linear in v (it carries the constant leak from non-ids_ sources) --
441 // multiply() below fixes that by subtracting baseline_ = RawMultiply(0).
442 Eigen::VectorXd RawMultiply(const Eigen::VectorXd& v) const;
443
444 // Adds the intramolecular erfc-screened, Thole-damped contribution
445 // (see class documentation for the fuller history) into result in
446 // place, for every segment in ids_ with more than one site. This term
447 // is linear in v and contributes 0 at v=0, so it is folded directly
448 // into RawMultiply's own result rather than needing any separate
449 // baseline_ handling.
450 void AddIntraSegmentCoupling(const Eigen::VectorXd& v,
451 Eigen::VectorXd& result) const;
452
453 // Global vector index i's (segment index within ids_, site index
454 // within that segment) -- the reverse of the offset table below,
455 // found by binary search (std::upper_bound) on offsets_. Used only by
456 // operator()(i,j), which is not performance-critical (see class
457 // documentation).
458 std::pair<Index, Index> LocateSite(Index i) const;
459
464 std::vector<Index> ids_;
465 // Used for its own public ComputeB (erfc-screened B-functions) AND
466 // ComputeThole (Thole damping factors), constructed with the REAL
467 // thole_a passed to this class's own constructor -- unlike an earlier
468 // version of this class (see AddIntraSegmentCoupling's own
469 // documentation), ComputeThole genuinely is called from this class
470 // now, so thole_a here must be the real value, not a placeholder.
472 // offsets_[n] is the global vector index (in units of a single scalar,
473 // not a 3-vector) where segment ids_[n]'s own block starts;
474 // offsets_[n+1]-offsets_[n] = 3 * (that segment's own site count).
475 // offsets_.size() == ids_.size()+1, with offsets_.back() == size_.
476 std::vector<Index> offsets_;
478 Eigen::VectorXd baseline_;
480 // The position-independent 3x3 matrix M such that a site's own trial
481 // dipole v_i produces a spurious reciprocal-space self-field -M*v_i at
482 // its own position (see EwaldReciprocalSpaceSum::SelfFieldMatrix's own
483 // documentation for the derivation). Legacy explicitly removes this
484 // same leak for the induced-dipole case (EwdInteractor::
485 // FU12_ERF_At_By, called with the SAME site passed as both arguments
486 // -- confirmed by tracing PolarBackground's own SOR iteration
487 // directly -- with its own explicit "Note the (-): This is a
488 // compensation term" comment); this class did not, until this was
489 // traced down as the explanation for a large, carbon-specific
490 // discrepancy that survived fixing the (separate) intramolecular
491 // static-static leak. Computed once here (position-independent, same
492 // for every site, depends only on the k-vector set/alpha/volume) and
493 // added back into RawMultiply's own result for every site, cancelling
494 // that site's own -M*v_i leak already present via site.V() (recip_sum_
495 // never excludes anything, self included -- see its own class
496 // documentation).
497 Eigen::Matrix3d self_field_matrix_;
498};
499
500} // namespace xtp
501} // namespace votca
502
503namespace Eigen {
504namespace internal {
505template <typename Vtype>
506struct generic_product_impl<votca::xtp::EwaldPeriodicDipoleOperator, Vtype,
507 DenseShape, DenseShape, GemvProduct>
508 : generic_product_impl_base<
509 votca::xtp::EwaldPeriodicDipoleOperator, Vtype,
510 generic_product_impl<votca::xtp::EwaldPeriodicDipoleOperator,
511 Vtype>> {
512 typedef
513 typename Product<votca::xtp::EwaldPeriodicDipoleOperator, Vtype>::Scalar
515
516 template <typename Dest>
517 static void scaleAndAddTo(Dest& dst,
519 const Vtype& v, const Scalar& alpha) {
520 assert(alpha == Scalar(1) && "scaling is not implemented");
521 EIGEN_ONLY_USED_FOR_DEBUG(alpha);
522 Eigen::VectorXd temp = op.multiply(v);
523 dst = temp.cast<Scalar>();
524 }
525};
526} // namespace internal
527} // namespace Eigen
528
529#endif // VOTCA_XTP_EWALDPERIODICDIPOLEOPERATOR_H
InnerIterator(const EwaldPeriodicDipoleOperator &xpr, const Index &id)
std::pair< Index, Index > LocateSite(Index i) const
Eigen::Product< EwaldPeriodicDipoleOperator, Vtype, Eigen::AliasFreeProduct > operator*(const Eigen::MatrixBase< Vtype > &x) const
void AddIntraSegmentCoupling(const Eigen::VectorXd &v, Eigen::VectorXd &result) const
Eigen::VectorXd RawMultiply(const Eigen::VectorXd &v) const
EwaldPeriodicDipoleOperator(EwaldRegistry &registry, const EwaldRealSpaceSum &real_sum, const EwaldReciprocalSpaceSum &recip_sum, const EwaldShapeCorrection &shape, std::vector< Index > ids, double alpha_ewald, double thole_a)
Eigen::VectorXd multiply(const Eigen::VectorXd &v) const
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
static void scaleAndAddTo(Dest &dst, const votca::xtp::EwaldPeriodicDipoleOperator &op, const Vtype &v, const Scalar &alpha)