use crate::prelude::*;
pub fn is_dir_format(fmt: &str) -> bool {
fmt.to_lowercase().starts_with("dir-")
}
pub fn strip_dir_prefix(fmt: &str) -> &str {
let fmt_lower = fmt.to_lowercase();
if fmt_lower.starts_with("dir-") {
&fmt[4..]
} else {
fmt
}
}
#[derive(Clone, Copy)]
pub struct WriterInfo {
pub name: &'static str,
pub display: &'static str,
pub extension: &'static str,
pub comment: &'static str,
pub valid: &'static [&'static str],
pub aliases: &'static [&'static str],
}
impl WriterInfo {
pub fn extension_without_dot(&self) -> &'static str {
self.extension.trim_start_matches('.')
}
pub fn supports_function_types(&self, ftypes: &HashSet<String>) -> bool {
if self.valid.is_empty() {
return true;
}
let valid_types: HashSet<String> = self.valid.iter().map(|s| s.to_string()).collect();
ftypes.is_subset(&valid_types)
}
pub fn is_alias(&self, name: &str) -> bool {
self.aliases.iter().any(|a| a.eq_ignore_ascii_case(name))
}
}
#[derive(Clone, Copy)]
struct WriterEntry {
name: &'static str,
display: &'static str,
extension: &'static str,
comment: &'static str,
valid: &'static [&'static str],
names: &'static [&'static str],
function: fn(&BseBasis) -> String,
}
const WRITER_ENTRIES: &[WriterEntry] = &[
WriterEntry {
name: "nwchem",
display: "NWChem",
extension: ".nw",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["nwchem", "nw"],
function: writers::nwchem::write_nwchem,
},
WriterEntry {
name: "gaussian94",
display: "Gaussian",
extension: ".gbs",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["gaussian94", "g94", "gaussian", "gau"],
function: writers::g94::write_g94,
},
WriterEntry {
name: "gaussian94lib",
display: "Gaussian, system library",
extension: ".gbs",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["gaussian94lib", "g94lib"],
function: writers::g94::write_g94lib,
},
WriterEntry {
name: "psi4",
display: "Psi4",
extension: ".gbs",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["psi4"],
function: writers::g94::write_psi4,
},
WriterEntry {
name: "molcas",
display: "Molcas",
extension: ".molcas",
comment: "*",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["molcas"],
function: writers::molcas::write_molcas,
},
WriterEntry {
name: "molcas_library",
display: "Molcas basis library",
extension: ".molcas",
comment: "*",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["molcas_library"],
function: writers::molcas_library::write_molcas_library,
},
WriterEntry {
name: "qchem",
display: "Q-Chem",
extension: ".qchem",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["qchem"],
function: writers::qchem::write_qchem,
},
WriterEntry {
name: "orca",
display: "ORCA",
extension: ".orca",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["orca"],
function: writers::orca::write_orca,
},
WriterEntry {
name: "dalton",
display: "Dalton",
extension: ".dalton",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["dalton"],
function: writers::dalton::write_dalton,
},
WriterEntry {
name: "qcschema",
display: "QCSchema",
extension: ".json",
comment: "",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["qcschema"],
function: writers::qcschema::write_qcschema,
},
WriterEntry {
name: "cp2k",
display: "CP2K",
extension: ".cp2k",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["cp2k"],
function: writers::cp2k::write_cp2k,
},
WriterEntry {
name: "pqs",
display: "PQS",
extension: ".pqs",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["pqs"],
function: writers::pqs::write_pqs,
},
WriterEntry {
name: "demon2k",
display: "deMon2K",
extension: ".d2k",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["demon2k", "d2k"],
function: writers::demon2k::write_demon2k,
},
WriterEntry {
name: "gamess_us",
display: "GAMESS US",
extension: ".bas",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["gamess_us"],
function: writers::gamess_us::write_gamess_us,
},
WriterEntry {
name: "turbomole",
display: "Turbomole",
extension: ".tm",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["turbomole", "tm"],
function: writers::turbomole::write_turbomole,
},
WriterEntry {
name: "gamess_uk",
display: "GAMESS UK",
extension: ".bas",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["gamess_uk"],
function: writers::gamess_uk::write_gamess_uk,
},
WriterEntry {
name: "molpro",
display: "Molpro",
extension: ".mpro",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["molpro", "mpro"],
function: writers::molpro::write_molpro,
},
WriterEntry {
name: "libmol",
display: "Molpro system library",
extension: ".libmol",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["libmol"],
function: writers::libmol::write_libmol,
},
WriterEntry {
name: "cfour",
display: "CFOUR",
extension: ".c4bas",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["cfour", "c4bas"],
function: writers::genbas::write_cfour,
},
WriterEntry {
name: "acesii",
display: "ACES II",
extension: ".acesii",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["acesii"],
function: writers::genbas::write_aces2,
},
WriterEntry {
name: "xtron",
display: "xTron",
extension: ".gbs",
comment: "!",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["xtron"],
function: writers::g94::write_xtron,
},
WriterEntry {
name: "bsedebug",
display: "BSE Debug",
extension: ".bse",
comment: "!",
valid: &[],
names: &["bsedebug"],
function: writers::bsedebug::write_bsedebug,
},
WriterEntry {
name: "json",
display: "JSON",
extension: ".json",
comment: "",
valid: &[],
names: &["json", "bsejson"],
function: writers::bsejson::write_bsejson,
},
WriterEntry {
name: "bdf",
display: "BDF",
extension: ".bdf",
comment: "*",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["bdf"],
function: writers::bdf::write_bdf,
},
WriterEntry {
name: "ricdwrap",
display: "Wrapper for generating acCD auxiliary basis sets with OpenMolcas",
extension: ".ricdwrap",
comment: "*",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["ricdwrap"],
function: writers::ricdwrap::write_ricdwrap,
},
WriterEntry {
name: "fhiaims",
display: "FHI-aims",
extension: ".fhiaims",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical"],
names: &["fhiaims"],
function: writers::fhiaims::write_fhiaims,
},
WriterEntry {
name: "jaguar",
display: "Jaguar",
extension: ".jaguar",
comment: "#",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["jaguar"],
function: writers::jaguar::write_jaguar,
},
WriterEntry {
name: "crystal",
display: "Crystal",
extension: ".crystal",
comment: "*",
valid: &["gto", "gto_cartesian", "gto_spherical", "scalar_ecp"],
names: &["crystal"],
function: writers::crystal::write_crystal,
},
WriterEntry {
name: "veloxchem",
display: "VeloxChem",
extension: ".vlx",
comment: "!",
valid: &["gto", "gto_spherical"],
names: &["veloxchem", "vlx"],
function: writers::veloxchem::write_veloxchem,
},
];
lazy_static::lazy_static! {
static ref WRITER_MAP: HashMap<String, usize> = {
let mut map = HashMap::new();
for (idx, entry) in WRITER_ENTRIES.iter().enumerate() {
for name in entry.names {
map.insert(name.to_lowercase(), idx);
}
}
map
};
static ref WRITER_EXTENSION_MAP: HashMap<String, &'static str> = {
let mut map = HashMap::new();
for entry in WRITER_ENTRIES.iter() {
let ext = entry.extension.trim_start_matches('.').to_lowercase();
map.entry(ext).or_insert(entry.name);
}
map
};
}
fn get_writer_entry(fmt: &str) -> Option<&WriterEntry> {
let fmt_lower = fmt.to_lowercase();
WRITER_MAP.get(&fmt_lower).map(|idx| &WRITER_ENTRIES[*idx])
}
pub fn get_writer_format_by_extension(ext: &str) -> Option<&'static str> {
let ext_lower = ext.to_lowercase();
WRITER_EXTENSION_MAP.get(&ext_lower).copied()
}
pub fn get_writer_info(fmt: &str) -> Option<WriterInfo> {
get_writer_entry(fmt).map(|e| WriterInfo {
name: e.name,
display: e.display,
extension: e.extension,
comment: e.comment,
valid: e.valid,
aliases: e.names,
})
}
pub fn get_writer_formats(function_types: Option<Vec<String>>) -> HashMap<String, String> {
let ftypes: Option<HashSet<String>> = function_types.map(|ft| ft.into_iter().map(|s| s.to_lowercase()).collect());
WRITER_ENTRIES
.iter()
.filter(|entry| {
if let Some(ft) = &ftypes {
let valid_types: HashSet<String> = entry.valid.iter().map(|s| s.to_string()).collect();
ft.is_subset(&valid_types)
} else {
true
}
})
.map(|entry| (entry.name.to_string(), entry.display.to_string()))
.collect()
}
pub fn get_writer_formats_with_aliases(
function_types: Option<Vec<String>>,
) -> HashMap<String, (String, String, Vec<String>)> {
let ftypes: Option<HashSet<String>> = function_types.map(|ft| ft.into_iter().map(|s| s.to_lowercase()).collect());
WRITER_ENTRIES
.iter()
.filter(|entry| {
if let Some(ft) = &ftypes {
let valid_types: HashSet<String> = entry.valid.iter().map(|s| s.to_string()).collect();
ft.is_subset(&valid_types)
} else {
true
}
})
.map(|entry| {
let aliases: Vec<String> =
entry.names.iter().filter(|n| **n != entry.name).map(|n| n.to_string()).collect();
(entry.name.to_string(), (entry.display.to_string(), entry.extension.to_string(), aliases))
})
.collect()
}
pub fn get_format_extension(fmt: &str) -> Result<&'static str, BseError> {
get_writer_entry(fmt)
.map(|e| e.extension)
.ok_or_else(|| BseError::ValueError(format!("Unknown basis set format '{}'", fmt)))
}
pub fn write_formatted_basis_str(basis_dict: &BseBasis, fmt: &str, header: Option<&str>) -> String {
write_formatted_basis_str_f(basis_dict, fmt, header).unwrap()
}
pub fn write_formatted_basis_str_f(basis_dict: &BseBasis, fmt: &str, header: Option<&str>) -> Result<String, BseError> {
let fmt_lower = fmt.to_lowercase();
let entry =
get_writer_entry(&fmt_lower).ok_or_else(|| BseError::ValueError(format!("Unknown writer format: {}", fmt)))?;
if !entry.valid.is_empty() {
let ftypes: HashSet<String> = basis_dict.function_types.iter().cloned().collect();
let valid_types: HashSet<String> = entry.valid.iter().map(|s| s.to_string()).collect();
if !ftypes.is_subset(&valid_types) {
return bse_raise!(
ValueError,
"Writer {} does not support all function types: {:?} vs {:?}",
entry.display,
basis_dict.function_types,
entry.valid
);
}
}
let mut ret_str = (entry.function)(basis_dict);
if let Some(header) = header {
if !entry.comment.is_empty() {
let comment_str = entry.comment;
let header_str = header.split('\n').map(|line| format!("{comment_str}{line}")).join("\n");
if fmt_lower == "gaussian94lib" {
ret_str.insert_str(0, &header_str);
} else {
ret_str.insert_str(0, &format!("{header_str}\n\n"));
}
}
}
if fmt_lower == "psi4" {
let types = &basis_dict.function_types;
let harm_type = if types.contains(&"gto_cartesian".to_string()) { "cartesian" } else { "spherical" };
ret_str.insert_str(0, &format!("{harm_type}\n\n"));
}
Ok(ret_str)
}