def overlay_cp2k_settings(self, cp2k_settings: MutableMapping, psf: Optional[PSFContainer] = None) -> None: r"""Overlay **df** with all :math:`q`, :math:`\sigma` and :math:`\varepsilon` values from **cp2k_settings**.""" # noqa charge = cp2k_settings['input']['force_eval']['mm']['forcefield'][ 'charge'] charge_dict = { block['atom']: float(block['charge']) for block in charge } if psf is not None: psf_charge_dict = dict(zip(psf.atom_type, psf.charge)) for k, v in psf_charge_dict.items(): if k not in charge_dict: charge_dict[k] = v epsilon_s = Settings() # type: ignore[var-annotated] sigma_s = Settings() # type: ignore[var-annotated] # Check if the settings are qmflows-style generic settings lj = cp2k_settings.get('lennard-jones') or cp2k_settings.get( 'lennard_jones') if lj is not None: self._overlay_s_qmflows(cp2k_settings, sigma_s, epsilon_s) else: lj = cp2k_settings['input']['force_eval']['mm']['forcefield'][ 'nonbonded']['lennard-jones'] # noqa self._overlay_s_plams(lj, sigma_s, epsilon_s) self.set_charge(charge_dict) for unit, dct in epsilon_s.items(): self.set_epsilon_pairs(dct, unit=unit) for unit, dct in sigma_s.items(): self.set_sigma_pairs(dct, unit=unit)
def get_template(template_name: str, from_cat_data: bool = True) -> Settings: """Grab a yaml template and return it as Settings object.""" if from_cat_data: path = join('data/templates', template_name) xs = pkg.resource_string('CAT', path) return Settings(yaml.load(xs.decode(), Loader=yaml.FullLoader)) with open(template_name, 'r') as file: return Settings(yaml.load(file, Loader=yaml.FullLoader))
def qd_opt(mol: Molecule, jobs: Tuple[Optional[Type[Job]], ...], settings: Tuple[Optional[Settings], ...], use_ff: bool = False) -> None: """Perform an optimization of the quantum dot. Performs an inplace update of **mol**. Parameters ---------- mol : |plams.Molecule|_ The to-be optimized molecule. job_recipe : |plams.Settings|_ A Settings instance containing all jon settings. Expects 4 keys: ``"job1"``, ``"job2"``, ``"s1"``, ``"s2"``. forcefield : bool If ``True``, perform the job with CP2K with a user-specified forcefield. """ # Prepare the job settings if use_ff: qd_opt_ff(mol, jobs, settings) return None # Expand arguments job1, job2 = jobs s1, s2 = settings # Extra options for AMSJob if job1 is AMSJob: s1 = Settings(s1) s1.input.ams.constraints.atom = mol.properties.indices if job2 is AMSJob: s2 = Settings(s2) s2.input.ams.constraints.atom = mol.properties.indices # Run the first job and fix broken angles mol.job_geometry_opt(job1, s1, name='QD_opt_part1') fix_carboxyl(mol) fix_h(mol) mol.round_coords() # Run the second job if job2 is not None: mol.job_geometry_opt(job2, s2, name='QD_opt_part2') mol.round_coords() return None
def frequencies(): s = Settings() s.input.ams.properties.NormalModes = 'Yes' s.input.ams.Properties.PESPointCharacter = 'No' s.input.ams.NormalModes.ReScanFreqRange = '-1000 0' s.input.ams.PESPointCharacter.NegativeEigenvalueTolerance = -0.001 return s
def set_header(s: Settings, *values: str) -> None: """Assign *value* to the ``["_h"]`` key in *s.input.compound*.""" s.input.compound = [] for item in values: s.input.compound.append(Settings({'_h': item})) s.input.compound[ 0].frac1 = 1.0 # The first item in *values should be the solvent
def get_surface_charge(mol: Molecule, job: Type[Job], s: Settings) -> Optional[str]: """Construct the COSMO surface of the **mol**. Parameters ---------- mol : |plams.Molecule|_ A PLAMS Molecule. job : |Callable|_ A type Callable of a class derived from :class:`Job`, e.g. :class:`AMSJob` or :class:`Cp2kJob`. s : |plams.Settings|_ The settings for **job**. Returns ------- |plams.Settings|_ Optional: The path+filename of a file containing COSMO surface charges. """ s = Settings(s) # Special procedure for ADF jobs # Use the gas-phase electronic structure as a fragment for the COSMO single point if job is ADFJob: s = get_surface_charge_adf(mol, job, s) s.runscript.post = '$ADFBIN/cosmo2kf "mopac.cos" "mopac.coskf"' results = mol.job_single_point(job, s, ret_results=True) return get_coskf(results)
def recreate_settings(self) -> Settings: """Construct a |Settings| instance from ``"$JN.run"``.""" runfile = self['$JN.run'] # Ignore the first 2 lines with open(runfile, 'r') as f: for i in f: if 'MATCH.pl' in i: args = next(f).split() break else: raise FileError( f"Failed to parse the content of ...{os.sep}{runfile!r}") # Delete the executable and pop the .pdb filename del args[0] pdb_file = args.pop(-1) s = Settings() for k, v in zip(args[0::2], args[1::2]): k = k[1:].lower() s.input[k] = v s.input.filename = pdb_file return s
def set_cp2k_param(settings: Settings, param_dict: dict) -> None: """Placeholder.""" for block_name, block in param_dict.items(): # Create a to-be formatted string with user-specified units unit = f'[{block.unit}]' + ' {}' if 'unit' in block else '{}' # Get the to-be update list of settings s = settings.get_nested(block['keys']) if not isinstance(s, list): _s = settings.get_nested(block['keys'][:-1]) s = _s[block['keys'][-1]] = [] for k, v in block.items(): if k in ('keys', 'unit'): # Skip continue value = unit.format(v) atom = 'atoms' if len(k.split()) > 1 else 'atom' atom_list = [i[atom] for i in s] try: # Intersecting set idx = atom_list.index(k) s[idx].update({block_name: value}) except ValueError: # Disjoint set new_block = Settings({atom: k, block_name: value}) s.append(new_block)
def DFTB(): s = Settings() s.input.ams.task = 'GeometryOptimization' s.input.DFTB s.input.DFTB.Model = "GFN1-xTB" s.input.ams.System.Charge = 0 return s
def update_adf_defaults(ams_settings: Settings) -> None: """Add the COSMO-RS compound defaults to *ams_settings*, returning a copy of the new settings. The engine block (*ams_settings.input.adf*) is soft updated with the following Settings: .. code:: Engine ADF Basis Type TZP Core Small End XC GGA BP86 End Relativity Level Scalar End BeckeGrid Quality Good End EndEngine """ # Find the solvation key # solvation = ams_settings.input.adf.find_case('solvation') # solvation_block = ams_settings.input.adf[solvation] adf = ams_settings.input.find_case('adf') adf_block = ams_settings.input[adf] # Find all keys for within the adf block keys = ('basis', 'xc', 'relativity', 'BeckeGrid') basis, xc, relativity, BeckeGrid = [ adf_block.find_case(item) for item in keys ] # Construct the default solvation block adf_defaults_block = Settings({ basis: { 'Type': 'TZP', 'Core': 'Small' }, xc: { 'GGA': 'BP86' }, relativity: { 'Level': 'Scalar' }, BeckeGrid: { 'Quality': 'Good' } }) # Copy ams_settings and perform a soft update ret = ams_settings.copy() ret.input.adf.soft_update(adf_defaults_block) return ret
def test_settings_to_molecule(): s = Settings() s.input.ams.system.Atoms._1 = ' H 0.0000000000 0.0000000000 0.0000000000 ' s.input.ams.system.Atoms._2 = ' O 1.0000000000 0.0000000000 0.0000000000 ' t = s.copy() mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input() s = Settings() s.input.ams.system.Atoms._1 = [ ' H 0.0000000000 0.0000000000 0.0000000000 ', ' O 1.0000000000 0.0000000000 0.0000000000 ' ] t = s.copy() mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input() #test for headers: s.input.ams.system.Atoms._h = '[A]' s.input.ams.system.Atoms._1 = [ ' O -0.0509000000 -0.2754000000 0.6371000000 ForceField.Charge=-0.834 ForceField.Type=OW', ' H 0.0157000000 0.5063000000 0.0531000000 ForceField.Charge=0.417 ForceField.Type=HW', ' H -0.0055000000 -1.0411000000 0.0658000000 ForceField.Charge=0.417 ForceField.Type=HW', ' O 0.0981000000 1.7960000000 -1.2550000000 ForceField.Charge=-0.834 ForceField.Type=OW', ' H -0.6686000000 2.2908000000 -1.5343000000 ForceField.Charge=0.417 ForceField.Type=HW', ' H 0.8128000000 2.3488000000 -1.5619000000 ForceField.Charge=0.417 ForceField.Type=HW', ] t = s.copy() #currently plams completely ignores the [A] unit specifier, it might also not print it if it does #get implemented and instead just convert the values in the molecule. del s.input.ams.system.Atoms._h mol = AMSJob.settings_to_mol(t)[''] assert AMSJob(settings=s).get_input() == AMSJob(settings=t, molecule=mol).get_input()
def get_recipe(self) -> Dict[Tuple[str], JobRecipe]: """Create a recipe for :meth:`WorkFlow.to_db`.""" settings_names = [i[1:] for i in self.export_columns if i[0] == 'settings'] uff_fallback = { 'key': f'RDKit_{rdkit.__version__}', 'value': f'{UFF.__module__}.{UFF.__name__}' } ret: Dict[Tuple[str], JobRecipe] = Settings() for name, job, settings in zip(settings_names, self.jobs, self.settings): # job is None, *i.e.* it's an RDKit UFF optimziation if job is None: ret[name].update(uff_fallback) # type: ignore continue settings = Settings(settings) if self.read_template: # Update the settings using a QMFlows template template = qmflows.geometry['specific'][self.type_to_string(job)].copy() settings.soft_update(template) ret[name]['key'] = job ret[name]['value'] = settings return ret
def get_pka(mol: Molecule, coskf_mol: Optional[str], coskf_mol_conj: Optional[str], water: str, hydronium: str, job: Type[Job], s: Settings) -> List[float]: if coskf_mol is None: return 5 * [np.nan] elif coskf_mol_conj is None: return 5 * [np.nan] s = Settings(s) s.input.compound[1]._h = water s.ignore_molecule = True s_dict = {} for name, coskf in _iter_coskf(coskf_mol_conj, coskf_mol, water, hydronium): _s = s.copy() _s.name = name _s.input.compound[0]._h = coskf s_dict[name] = _s # Run the job mol_name = mol.properties.name job_list = [CRSJob(settings=s, name=name) for name, s in s_dict.items()] results_list = [_crs_run(job, mol_name) for job in job_list] # Extract solvation energies and activity coefficients E_solv = {} for name, results in zip(("acid", "base", "solvent", "solvent_conj"), results_list): results.wait() try: E_solv[name] = _E = results.get_energy() assert _E is not None logger.info(f'{results.job.__class__.__name__}: {mol_name} pKa ' f'calculation ({results.job.name}) is successful') except Exception: logger.error(f'{results.job.__class__.__name__}: {mol_name} pKa ' f'calculation ({results.job.name}) has failed') E_solv[name] = np.nan try: mol.properties.job_path += [ join(job.path, job.name + '.in') for job in job_list ] except IndexError: # The 'job_path' key is not available mol.properties.job_path = [ join(job.path, job.name + '.in') for job in job_list ] ret = [E_solv[k] for k in ("acid", "base", "solvent", "solvent_conj")] ret.append(_get_pka(**E_solv)) return ret
def overlay_cp2k_settings(self, cp2k_settings: Settings) -> None: """Extract forcefield information from PLAMS-style CP2K settings. Performs an inplace update of this instance. Examples -------- Example input value for **cp2k_settings**. In the provided example the **cp2k_settings** are directly extracted from a CP2K .inp file. .. code:: python >>> import cp2kparser # https://github.com/nlesc-nano/CP2K-Parser >>> filename = str(...) >>> cp2k_settings: dict = cp2kparser.read_input(filename) >>> print(cp2k_settings) {'force_eval': {'mm': {'forcefield': {'nonbonded': {'lennard-jones': [...]}}}}} Parameters ---------- cp2k_settings : :class:`~collections.abc.Mapping` A Mapping with PLAMS-style CP2K settings. """ if 'input' not in cp2k_settings: cp2k_settings = Settings({'input': cp2k_settings}) # If cp2k_settings is a Settings instance enable the `suppress_missing` context manager # In this manner normal KeyErrors will be raised, just like with dict if isinstance(cp2k_settings, Settings): context_manager = cp2k_settings.suppress_missing else: context_manager = nullcontext with context_manager(): for prm_map in self.CP2K_TO_PRM.values(): name = prm_map['name'] columns = list(prm_map['columns']) key_path = prm_map['key_path'] key = prm_map['key'] unit = prm_map['unit'] default_unit = prm_map['default_unit'] post_process = prm_map['post_process'] self._overlay_cp2k_settings(cp2k_settings, name, columns, key_path, key, unit, default_unit, post_process)
def extract_args(args: Optional[List[str]] = None) -> Settings: """Extract and return all arguments.""" input_file = args.YAML[0] if exists(input_file): pass elif exists(join(getcwd(), input_file)): input_file = join(getcwd(), input_file) else: input_file2 = join(getcwd(), input_file) raise FileNotFoundError( f'No file found at {input_file} or {input_file2}') with open(input_file, 'r') as file: return Settings(yaml.load(file, Loader=yaml.FullLoader))
def update_ff_jobs(s: Settings) -> None: """Update forcefield settings.""" if NANO_CAT is not None: raise NANO_CAT ff = Settings() set_cp2k_param(ff, s.optional.forcefield) optimize = s.optional.qd.optimize if optimize and optimize.use_ff: if optimize.job1 and str(optimize.job1) == str(Cp2kJob): optimize.s1 = Settings() if optimize.s1 is None else optimize.s1 optimize.s1 += get_template('qd.yaml')['CP2K_CHARM_opt'] optimize.s1 += ff if optimize.job2 and str(optimize.job1) == str(Cp2kJob): optimize.s2 = Settings() if optimize.s2 is None else optimize.s2 optimize.s2 += get_template('qd.yaml')['CP2K_CHARM_opt'] optimize.s2 += ff dissociate = s.optional.qd.dissociate if dissociate and dissociate.use_ff: if dissociate.job1 and str(dissociate.job1) == str(Cp2kJob): dissociate.s1 = Settings( ) if dissociate.s1 is None else dissociate.s1 dissociate.s1 += get_template('qd.yaml')['CP2K_CHARM_opt'] dissociate.s1 += ff activation_strain = s.optional.qd.activation_strain if activation_strain and activation_strain.use_ff: if activation_strain.job1 and str( activation_strain.job1) == str(Cp2kJob): key = 'CP2K_CHARM_singlepoint' if not activation_strain.md else 'CP2K_CHARM_md' activation_strain.s1 = Settings( ) if activation_strain.s1 is None else activation_strain.s1 # noqa activation_strain.s1 += get_template('qd.yaml')[key] activation_strain.s1 += ff
def DFT(): s = Settings() s.input.ams.task = 'GeometryOptimization' s.input.adf.basis.type = 'TZ2P' s.input.adf.basis.core = 'None' s.input.adf.xc.hybrid = 'B3LYP' s.input.adf.xc.Dispersion = 'GRIMME3 BJDAMP' s.input.adf.Relativity.Level = 'None' s.input.adf.NumericalQuality = 'Good' s.input.adf.Symmetry = 'NOSYM' s.input.ams.UseSymmetry = 'No' s.input.adf.Unrestricted = 'No' s.input.adf.SpinPolarization = 0 s.input.ams.System.Charge = 0 return s
def read_mol_folder(mol_dict: Settings) -> Optional[List[Molecule]]: """Read all files (.xyz, .pdb, .mol, .txt or further subfolders) within a folder.""" try: mol_type = 'input_cores' if mol_dict.is_core else 'input_ligands' _file_list = os.listdir(mol_dict.mol) optional_dict = Settings( {k: v for k, v in mol_dict.items() if k not in ('mol', 'path')}) file_list = [{i: optional_dict} for i in _file_list] validate_mol(file_list, mol_type, mol_dict.path) return read_mol(file_list) except Exception as ex: print_exception('read_mol_folder', mol_dict.name, ex)
def start_crs_jobs(mol_list: Iterable[Molecule], jobs: Iterable[Type[Job]], settings: Iterable[Settings], **kwargs: Any) -> List[pd.Series]: # Parse the job type job, *_ = jobs if job is not ADFJob: raise NotImplementedError(f"job: {job.__class__.__name__} = {job!r}") # Parse and update the input settings _s, *_ = settings s = Settings(_s) s.input += cdft.specific.adf ret = [] for mol in mol_list: ret.append(run_cdft_job(mol, job, s)) return ret
def _get_logp(solutes: _NDArray[np.object_]) -> _NDArray[np.float64]: """Perform a LogP calculation.""" ret = np.full_like(solutes, np.nan, dtype=np.float64) mask = solutes != None count = np.count_nonzero(mask) if count == 0: return ret s = copy.deepcopy(LOGP_SETTINGS) for v in s.input.compound[:2]: v._h = v._h.format(os.environ["AMSRESOURCES"]) s.input.compound += [Settings({"_h": f'"{sol}"', "_1": "compkffile"}) for sol in solutes[mask]] ret[mask] = _run_crs( s, count, logp=lambda r: r.readkf('LOGP', 'logp')[2:], ) return ret
def _start_ligand_jobs_plams(ligand_list: Iterable[Molecule], job: Type[Job], settings: Settings, charge_func: ChargeFunc) -> None: """Loop over all molecules in ``ligand_df.loc[idx]`` and perform geometry optimizations.""" for ligand in ligand_list: try: optimize_ligand(ligand) except Exception as ex: logger.debug(f'{ex.__class__.__name__}: {ex}', exc_info=True) ligand.properties.is_opt = False continue s = Settings(settings) charge_func(s, int(sum(at.properties.get('charge', 0) for at in ligand))) ligand.job_geometry_opt(job, s, name='ligand_opt') ligand.round_coords( ) # `.is_opt = True` is set by `ligand.job_geometry_opt()`` return None
def finalize_lj(mol: Molecule, s: List[Settings]) -> None: """Assign UFF Lennard-Jones parameters to all missing non-bonded core/ligand interactions. .. _LENNARD_JONES: https://manual.cp2k.org/trunk/CP2K_INPUT/FORCE_EVAL/MM/FORCEFIELD/NONBONDED/LENNARD-JONES.html Parameters ---------- mol : |plams.Molecule|_ A PLAMS molecule containing a core and ligand(s). s : |list|_ [|plams.Settings|_] A list of settings constructed from the CP2K FORCE_EVAL/MM/FORCEFIELD/NONBONDED/`LENNARD-JONES`_ block. The settings are expected to contain the ``"atoms"`` keys. """ # noqa # Create a set of all core atom types core_at, lig_at = _gather_core_lig_symbols(mol) # Create a set of all user-specified core/ligand LJ pairs if not s: s = [] elif isinstance(s, dict): s = [s] atom_pairs = {frozenset(s.atoms.split()) for s in s} # Check if LJ parameters are present for all atom pairs. # If not, supplement them with UFF parameters. for at1, symbol1 in core_at.items(): for at2, symbol2 in lig_at.items(): at1_at2 = {at1, at2} if at1_at2 in atom_pairs: continue s.append( Settings({ 'atoms': f'{at1} {at2}', 'epsilon': f'[kcalmol] {round(combine_epsilon(symbol1, symbol2), 4)}', 'sigma': f'[angstrom] {round(combine_sigma(symbol1, symbol2), 4)}' }))
def init_ff_assignment(ligand_df: SettingsDataFrame, forcefield: str = 'top_all36_cgenff_new') -> None: """Initialize the forcefield assignment procedure using MATCH_. .. _MATCH: http://brooks.chem.lsa.umich.edu/index.php?page=match&subdir=articles/resources/software Parameters ---------- ligand_df : |CAT.SettingsDataFrame|_ A DataFrame of ligands. forcefield : str The type of to-be assigned forcefield atom types. See the ``-Forcefield`` paramater in the MATCH_ user guide for more details. By default the allowed values are: * ``"top_all22_prot"`` * ``"top_all27_na"`` * ``"top_all35_carb"`` * ``"top_all35_ether"`` * ``"top_all36_cgenff"`` * ``"top_all36_cgenff_new"`` * ``"top_all36_lipid"`` See Also -------- MATCH publication: `MATCH: An atom-typing toolset for molecular mechanics force fields, J.D. Yesselman, D.J. Price, J.L. Knight and C.L. Brooks III, J. Comput. Chem., 2011 <https://doi.org/10.1002/jcc.21963>`_ :func:`.run_match_job`: Assign atom types and charges to **mol** based on the results of MATCH_. :class:`.MatchJob`: A :class:`Job` subclass for interfacing with MATCH_: Multipurpose Atom-Typer for CHARMM. """ # noqa workflow = WorkFlow.from_template(ligand_df, name='forcefield') workflow.jobs = (MatchJob, ) workflow.settings = (Settings({'input': {'forcefield': forcefield}}), ) workflow(start_ff_assignment, ligand_df, columns=[])
def get_settings(values): """ Retrieve settings from command line, and set them up """ settings = Settings() settings.input.XC.GGA = values['functional'] if values['dispersion'] is not None: settings.input.XC.DISPERSION = values['dispersion'] settings.input.BASIS.type = values['basisset'] settings.input.BASIS.core = values['frozencore'] settings.input.BASIS.createoutput = 'None' settings.input.NumericalQuality = values['integrationquality'] settings.input.RELATIVISTIC = values['relativistic'] + " ZORA" settings.input.AOMAT2FILE = '' settings.input.SAVE = 'TAPE15' settings.input.FULLFOCK = '' settings.input.NOPRINT = "LOGFILE" settings.input.SYMMETRY = "NOSYM" return settings
def _run_match( mol_list: Iterable[Molecule], forcefield: str = 'top_all36_cgenff_new' ) -> Tuple[List[str], PRMContainer]: """Run a :class:`MatchJob`, using **forcefield**, on all molecules in **mol_list**.""" s = Settings() s.input.forcefield = forcefield rtf_list = [] prm_list = [] for i, mol in enumerate(mol_list): job = MatchJob(molecule=mol, settings=s, name=f'match_job.{i}') results = job.run() rtf = results['$JN.rtf'] prm = results['$JN.prm'] rtf_list.append(rtf) prm_list.append(prm) prm_ = _concatenate_prm(prm_list) return rtf_list, prm_
def read_mol_txt(mol_dict: Settings) -> Optional[List[Molecule]]: """Read a plain text file containing one or more SMILES strings.""" try: row = 0 if 'row' not in mol_dict else mol_dict.row column = 0 if 'column' not in mol_dict else mol_dict.column mol_type = 'input_cores' if mol_dict.is_core else 'input_ligands' with open(mol_dict.mol, 'r') as f: iterator = itertools.islice(f, row, None) _file_list = [ i.rstrip('\n').split()[column] for i in iterator if i ] optional_dict = Settings( {k: v for k, v in mol_dict.items() if k not in ('mol', 'path')}) file_list = [{i: optional_dict} for i in _file_list] validate_mol(file_list, mol_type, mol_dict.path) return read_mol(file_list) except Exception as ex: print_exception('read_mol_txt', mol_dict.name, ex)
def pre_process_settings(mol: Molecule, s: Settings, job_type: Type[Job], template_name: str) -> Settings: """Update all :class:`Settings`, **s**, with those from a QMFlows template (see **job**).""" ret = Settings() type_key = type_to_string(job_type) ret.input = getattr(qmflows, template_name)['specific'][type_key].copy() ret.update(s) if job_type is AMSJob: mol.properties.pop('charge', None) # ret.input.ams.system.BondOrders._1 = adf_connectivity(mol) if 'uff' not in s.input: ret.input.ams.system.charge = int( sum(at.properties.get('charge', 0) for at in mol)) elif job_type is ADFJob: mol.properties.pop('charge', None) if not ret.input.charge: ret.input.charge = int( sum(at.properties.get('charge', 0) for at in mol)) return ret
def _get_kwarg_dict2(armc: ARMC) -> Settings: ret = Settings() # type: ignore[var-annotated] shape = armc.iter_len // armc.sub_iter_len, armc.sub_iter_len err_shape = shape + (len( armc.molecule), len(armc.pes_validation) // len(armc.molecule)) ret.aux_error.shape = err_shape ret.aux_error.dtype = np.float64 ret.aux_error.fillvalue = np.nan for key, partial in armc.pes_validation.items(): ref: pd.DataFrame = partial.ref # type: ignore[attr-defined] ret[key].shape = shape + get_shape(ref) ret[key].dtype = np.float64 ret[key].fillvalue = np.nan ret[f'{key}.ref'].shape = get_shape(ref) ret[f'{key}.ref'].dtype = np.float64 ret[f'{key}.ref'].data = ref ret[f'{key}.ref'].fillvalue = np.nan return ret
def _get_gamma_e( solutes: _NDArray[np.object_], solvent: str, solvent_name: str, ) -> _NDArray[np.float64]: """Perform an activity coefficient and solvation energy calculation.""" ret = np.full((len(solutes), 2), np.nan, dtype=np.float64) mask = solutes != None count = np.count_nonzero(mask) if count == 0: return ret s = copy.deepcopy(GAMMA_E_SETTINGS) s.input.compound[0]._h = f'"{solvent}"' s.input.compound += [Settings({"_h": f'"{sol}"', "_1": "compkffile"}) for sol in solutes[mask]] ret[mask] = _run_crs( s, count, solvent_name, activity_coefficient=lambda r: r.readkf('ACTIVITYCOEF', 'gamma')[1:], solvation_energy=lambda r: r.readkf('ACTIVITYCOEF', 'deltag')[1:], ) return ret
def test_get_non_bonded() -> None: """Test :func:`FOX.functions.lj_calculate.get_non_bonded`.""" psf = PATH / 'Cd68Se55_26COO_MD_trajec.psf' prm = PATH / 'Cd68Se55_26COO_MD_trajec.prm' mol = MultiMolecule.from_xyz(PATH / 'Cd68Se55_26COO_MD_trajec.xyz') s = Settings() s.input.force_eval.mm.forcefield.charge = [ {'atom': 'Cd', 'charge': 0.933347}, {'atom': 'Se', 'charge': -0.923076} ] s.input.force_eval.mm.forcefield.nonbonded['lennard-jones'] = [ {'atoms': 'Cd Cd', 'epsilon': '[kjmol] 0.310100', 'sigma': '[nm] 0.118464'}, {'atoms': 'Se Se', 'epsilon': '[kjmol] 0.426600', 'sigma': '[nm] 0.485200'}, {'atoms': 'Se Cd', 'epsilon': '[kjmol] 1.522500', 'sigma': '[nm] 0.294000'} ] elstat_df, lj_df = get_non_bonded(mol, psf, prm=prm, cp2k_settings=s) elstat_ref = np.load(PATH / 'get_non_bonded_elstat.npy') lj_ref = np.load(PATH / 'get_non_bonded_lj.npy') np.testing.assert_allclose(elstat_df, elstat_ref) np.testing.assert_allclose(lj_df, lj_ref)