Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save or read orbitals in text format, portable and readable by MADNESS group #249

Open
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

gitpeterwind
Copy link
Member

Orbitals can be saved and read in text format (ascii) files. Each end node is saved with info about depth and translation.
Use keyword write_orbitals_txt = T to save in this format.

Also introduced keywords bank_per_node and use_omp_num_threads after feedback from Moritz and Roberto. (see documentation)

NB: this PR builds on top of the Four Components PR and is to be used with the corresponding MRChem PR.

gitpeterwind and others added 30 commits April 25, 2024 19:21

void calcNode(MWNode<D> &node_o) override {
void calcNode(MWNode<D, double> &node_o) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment here as well

}

template void add<1>(double prec,
FunctionTree<1> &out,
template void add<1, double>(double prec,
Copy link
Contributor

Choose a reason for hiding this comment

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

yes, a macro would go a very long way to reduce the boilerplate also for these instantiations!

else defaultMetric[i][j] = 0.0;
}
}
if (metric == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

C++17 introduced std::optional to deal with arguments that may or may not be passed. We should use that here.

else defaultMetric[i][j] = 0.0;
}
}
if (metric == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same comment about std::optional applies also here.

else defaultMetric[i][j] = 0.0;
}
}
if (metric == nullptr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

std::optional also here.

@@ -308,6 +461,46 @@ template <int D> void apply(FunctionTree<D> &out, DerivativeOperator<D> &oper, F
print::separator(10, ' ');
}

template <int D> void apply(CompFunction<D> &out, DerivativeOperator<D> &oper, CompFunction<D> &inp, int dir, ComplexDouble metric[4][4]) {
//TODO: sums and not only each components independently
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, but I wait for the implementation until we have real examples: the solution could take several forms (when to introduce the multiplicative constants and the like). (I completed the comment)

apply(*out.CompD[ocomp], oper, *inp.CompD[icomp], dir);
if (std::norm(metric[icomp][ocomp] - 1.0) > MachinePrec) {
if(std::imag(metric[icomp][ocomp]) < MachinePrec) out.CompD[ocomp]->rescale(std::real(metric[icomp][ocomp]));
else out.func_ptr->data.c1[ocomp] *= metric[icomp][ocomp]; //TODO: multiply c1 in rescale?
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO still relevant?

Copy link
Member Author

Choose a reason for hiding this comment

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

I rephrased: // To consider: multiply c1 in rescale?
The "c1 constant" is somewhat new and it is not yet completely integrated into the logic. It is a "soft" multiplicative constant; soft meaning the same as the conjugate flag for a function: not explicitly applied before it is required.
So far it is used for the complex spin operators: we do not want to convert all real functions to complex just because there is a complex multiplicative factor in the operator.

@@ -66,12 +66,12 @@ namespace mrcpp {
*
*/
template <int D>
void map(double prec, FunctionTree<D> &out, FunctionTree<D> &inp, FMap fmap, int maxIter, bool absPrec) {
void map(double prec, FunctionTree<D, double> &out, FunctionTree<D, double> &inp, FMap<double, double> fmap, int maxIter, bool absPrec) {
Copy link
Contributor

Choose a reason for hiding this comment

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

is map not relevant when one has float or std::complex scalar types?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't know, but since it is not used I prefer to not implement before we know what we want to use it for.

Comment on lines 44 to 47
template <int D> ComplexDouble dot(FunctionTree<D, ComplexDouble> &bra,
FunctionTree<D, double> &ket);
template <int D> ComplexDouble dot(FunctionTree<D, double> &bra,
FunctionTree<D, ComplexDouble> &ket);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's enough to have one function, but with three template parameters (similarly could be done for all arithmetic functions, actually)

Suggested change
template <int D> ComplexDouble dot(FunctionTree<D, ComplexDouble> &bra,
FunctionTree<D, double> &ket);
template <int D> ComplexDouble dot(FunctionTree<D, double> &bra,
FunctionTree<D, ComplexDouble> &ket);
template <int D, typename T, typename U = T, typename V = decltype(std::declval<T>() * std::declval<U>())>
V dot(FunctionTree<D, T> &bra, FunctionTree<D, U> &ket);

you can see why I dislike implementing templates in .cpp files 😅 (cough cough @ilfreddy)

Note that in the C++ standard mixing float and std::complex<double> (or double and std::complex<float>) is not possible, so the above should be combined with a compile-time exclusion of such a possibility (through SFINAE or something) Another thing I have in my archives.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is nice!


template <int D> double dot_scaling(const FunctionNode<D> &bra, const FunctionNode<D> &ket);
template <int D> double dot_wavelet(const FunctionNode<D> &bra, const FunctionNode<D> &ket);
template <int D>
Copy link
Contributor

Choose a reason for hiding this comment

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

again, I think these signatures can be declared only once using the "trick" above.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did not use the trick for the dot_scaling and dot_wavelet, because with the conjugate flags it became messy.

Comment on lines 111 to 115
/** @brief Read a previously stusing MADNESS conventions for n, l and index order.ored tree assuming text/ASCII format,
* in a representation
* @param[in] file: File name
* @note This tree must have the exact same MRA the one that was saved(?)
*/
Copy link
Contributor

Choose a reason for hiding this comment

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

The docstring here seems a bit mangled. Can you fix it?

Comment on lines 131 to 134
if (std::abs(coord[d][0] + L) > TXT_thres) std::cout<<coord[d][0]<<" "<<L<<std::endl;;
if (std::abs(coord[d][0] + L) > TXT_thres) NOT_IMPLEMENTED_ABORT;
if (std::abs(coord[d][1] - L) > TXT_thres) std::cout<<coord[d][1]<<" "<<L<<std::endl;;
if (std::abs(coord[d][1] - L) > TXT_thres) NOT_IMPLEMENTED_ABORT;
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand why the 2nd and 4th statements

Copy link
Member Author

Choose a reason for hiding this comment

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

It did not seem to work when the "scaling factors" where not 1.0. At present the box must be with sizes which are powers of 2.

std::vector<int> &indices,
std::vector<int> &parent_indices,
std::vector<double> &scalefac,
int &max_index,
MWTree<D> &refTree,
std::vector<MWNode<D> *> *refNodes) {
MWTree<D, double> &refTree,
Copy link
Contributor

Choose a reason for hiding this comment

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

here (and some other places) you explicitly fix the scalar type of some function inputs. Why is that?

return out;
}

/*
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's fine to delete commented-out code.

#else
for (int i = start; i < start + size; i++) { sq_norm += c[i] * c[i]; }
#endif
//#ifdef HAVE_BLAS
Copy link
Contributor

Choose a reason for hiding this comment

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

commented-out code can be removed

@@ -146,7 +146,12 @@ template <int D> bool MultiResolutionAnalysis<D>::operator==(const MultiResoluti
* For more information about the meaning of equality for BoundingBox and ScalingBasis objets, see their respective classes.
*/
template <int D> bool MultiResolutionAnalysis<D>::operator!=(const MultiResolutionAnalysis<D> &mra) const {
return !(*this == mra);
if (this->basis != mra.basis) std::cout<<"diff basis "<<this->basis<<std::endl <<"and "<< mra.basis<<std::endl;
Copy link
Contributor

Choose a reason for hiding this comment

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

are the print statements needed here? if yes, maybe the should use the printer. Also, no need to check the condition twice, just do a non-inline if-statement.

Copy link
Contributor

@robertodr robertodr left a comment

Choose a reason for hiding this comment

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

  • Can you run clang-format on all the C++ files this PR changes?
  • When giving a default for the scalar type (e.g. typename T = double) I think it should be enough to do it on the parent classes. This is however just to save some typing :)
  • I think we should have compile-time checks on whether the scalar type T makes sense or not, e.g. only one of float, double, std::complex<float>, std::complex<double>. I have some code to do this that we can reuse.
  • For the metric arguments of some functions in apply we may use std::optional<Eigen::Matrix<std::complex<double>, 4, 4>>.
  • There's a lot more boilerplate in the explicit instantiations. The obvious solution is to not put the implementation in .cpp files, but leave everything in the header. @ilfreddy (and maybe @stigrj?) feels strongly against it, so we'll have to make do with some preprocessor macros and/or some metaprogramming tricks.
  • I think we should allow to mix scalar types, but it can be left for a future refinement, as it involves some tricky gymnastics with template parameters.

@@ -67,6 +67,7 @@ double matrix_norm_1(const Eigen::MatrixXd &M);
double matrix_norm_2(const Eigen::MatrixXd &M);

void apply_filter(double *out, double *in, const Eigen::MatrixXd &filter, int kp1, int kp1_dm1, double fac);
void apply_filter(ComplexDouble *out, ComplexDouble *in, const Eigen::MatrixXd &filter, int kp1, int kp1_dm1, double fac);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the two can be unified with if constexpr

Copy link
Contributor

@robertodr robertodr left a comment

Choose a reason for hiding this comment

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

  • in parallel.h/parallel.cpp I think the implementations for double and std::complex<double> could be unified, so we reduce a bit the boilerplate (and possibly extend to float)

@gitpeterwind
Copy link
Member Author

I have now included all the changes you proposed, or left a comment.
It is a pity that I did not use the if constexpr from the start. Actually I did, but then went back because it was not compliant with the std14.
About using float I am sceptical: for small system there is no point, and for large "HPC" systems the error would be too large. Roughly a HPC calculation would take 10¹² operation at least, giving sqrt(10⁻¹²) = 10⁻⁶ relative numerical error, 6 digits error of about 7 significant digits is not useful.
About reducing the boilerplate instantiations, I do not think it is important: it is not those parts that take time to implement, and they do not reduce code readability (while macros might!).

@ilfreddy
Copy link
Contributor

Does this include BOTH the complex number template AND the MADNESS input stuff?

@gitpeterwind
Copy link
Member Author

yes

@ilfreddy ilfreddy mentioned this pull request Jan 28, 2025
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.

3 participants