-
Notifications
You must be signed in to change notification settings - Fork 7
Description
In some recent work on the solvers I've come across some confusing aspects of the code with respect to CC solver calculations with or without solution of the lambda equations. It seems like we need to properly decide what functionality we support and how to control it, as at the moment the interaction of various options are definitely confusing.
Much of these changes stem from 085372b, where we removed t_as_lambda as a CCSD solver option. IIRC there was some discussion of what behaviour we wanted with these options between automatically applying this approximation if required (with warnings) vs failing with a NotCalculatedError.
I can't remember what the conclusion of this was- we currently do the former, so I'm going to assume that in the rest of this. The CCSD solver has the form
if self.opts.solve_lambda:
...
else:
self.log.info("Using Lambda=T approximation for Lambda-amplitudes.")
l1, l2 = self.solver.t1, self.solver.t2
so in the absence of solving the lambda equations the l1 and l2 values will be set to t1 and t2 regardless of anything else.
The confusion comes in when then looking at routines to calculate global wavefunction and density matrix values, where we seem to have a fair amount of code trying to implement the other approach, and allow calculation of the t_as_lambda=True expectation values when solve_lambda=True, but with inconsistent approaches to control this option.
Looking for instance at get_global_t1_rhf in ewf/amplitudes.py we have code expecting control of this approximation to be in the fragment option t_as_lambda (can be inherited from emb):
for x in emb.get_fragments(contributes=True, mpi_rank=mpi.rank, sym_parent=None):
pwf = x.results.pwf.restore().as_ccsd()
if for_dm2 and x.solver == 'MP2':
continue
t1x = pwf.l1 if (get_lambda and not x.opts.t_as_lambda) else pwf.t1
if t1x is None:
raise NotCalculatedError("Fragment %s" % x)
while in ewf/rdm.py various functions have a similar keyword argument for control (that doesn't default to emb.opts.t_as_lambda in actual calls):
def make_rdm1_ccsd_global_wf(emb, t_as_lambda=False, with_t1=True, svd_tol=1e-3, ovlp_tol=None, use_sym=True,
late_t2_sym=True, mpi_target=None, slow=False):
...
l2 = wfy.t2 if (t_as_lambda or fy_parent.solver == 'MP2') else wfy.l2
if l2 is None:
raise RuntimeError("No L2-amplitudes found for %s!" % fy)
In calculation of correlation energies this t_as_lambda option is the difference between the 'dm-t2only' and 'dm' energy functionals.
As far as I can tell thanks to the code in the CCSD solver it's impossible for either of these values to be None and result in an error?
However, the main concern comes when looking at code (thankfully usually in the the slow implementations of various functions) which uses
l1 = emb.get_global_l1() if not t_as_lambda else t1
l2 = emb.get_global_l2() if not t_as_lambda else t2
These calls actually become calls to get_global_t1_rhf in ewf/amplitudes.py above, so depend on fragment.opts.t_as_lambda. To obtain the actual l1 value here then requires t_as_lambda=False, emb.opts.t_as_lambda=False, and solve_lambda=True. Someone using this function could be forgiven for thinking that t_as_lambda=False should be sufficient without setting the other parameters (which all default to appropriate values).
I'd say the first thing decide is when in a calculation we want to apply this approximation (in the solver or expectation value calculation).
The next thing would be taking into account that we're automatically applying this approximation where necessary in functions to calculate expectation values. Something like force_t_as_lambda or no_t_as_lambda to make it clear that the result can sometimes already incorporate this approximation regardless of this parameter value might be a little more transparent.
This obviously goes hand-in-hand with ensuring that we have a consistent approach to this between different functions.
(Sorry for the wall of text- I was getting test failures in ewf/test_tailoring.py which I traced back to this ambiguity after some effort and thought it best to raise while I had my head around it!)