use clap::Subcommand;
use serde_json::json;
use crate::format::StructureFormat;
use crate::{Cli, CliError, output};
#[derive(Debug, Subcommand)]
pub(crate) enum BuildAction {
Graphene {
nx: u32,
ny: u32,
#[arg(long, default_value_t = 1)]
layers: u32,
#[arg(long, default_value_t = 3.35)]
interlayer: f64,
#[arg(long, default_value_t = 15.0)]
vacuum: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
C20 {
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
C60 {
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Twist {
n: u32,
m: u32,
theta: f64,
#[arg(long, default_value_t = 2)]
layers: u32,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Ribbon {
width: u32,
length: u32,
#[arg(long, default_value_t = 12.0)]
vacuum: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Ztc {
#[arg(long, default_value_t = 16)]
samples: usize,
#[arg(long, default_value_t = 12.0)]
cell: f64,
#[arg(long, default_value_t = 0.0)]
isovalue: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Schwarzite {
#[arg(long, default_value_t = 16)]
samples: usize,
#[arg(long, default_value_t = 12.0)]
cell: f64,
#[arg(long, default_value_t = 0.0)]
isovalue: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Carbon {
#[arg(long, default_value = "dia")]
net: String,
#[arg(long, default_value_t = 1)]
repeat_x: usize,
#[arg(long, default_value_t = 1)]
repeat_y: usize,
#[arg(long, default_value_t = 1)]
repeat_z: usize,
#[arg(long, default_value_t = 0.42)]
density: f64,
#[arg(long, default_value_t = 0.25)]
radius_ratio: f64,
#[arg(long, default_value_t = 0.38)]
coverage: f64,
#[arg(long, default_value_t = 0)]
seed: u64,
#[arg(long, default_value_t = 0.07)]
hydrogen: f64,
#[arg(long, default_value_t = 0.29)]
fluorine: f64,
#[arg(long, default_value_t = 0.044)]
oxygen: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Polymer {
smiles: String,
monomers: u32,
#[arg(long, default_value_t = 1.0)]
density: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
Nanotube {
n: i32,
m: i32,
#[arg(long, default_value_t = 1)]
length: u32,
#[arg(long, default_value_t = 10.0)]
vacuum: f64,
#[arg(long, value_name = "PATH")]
out: Option<String>,
#[arg(long, value_enum)]
format: Option<StructureFormat>,
},
}
pub(crate) struct GrapheneOptions<'a> {
pub nx: u32,
pub ny: u32,
pub layers: u32,
pub interlayer: f64,
pub vacuum: f64,
pub out: Option<&'a str>,
pub format: Option<StructureFormat>,
}
pub(crate) fn graphene(cli: &Cli, options: GrapheneOptions<'_>) -> Result<(), CliError> {
let structure = xtalforge::build::graphene(
options.nx,
options.ny,
options.layers,
options.interlayer,
options.vacuum,
)
.map_err(CliError::core)?;
output::emit_structure(cli, &structure, options.out, options.format)
}
pub(crate) fn nanotube(
cli: &Cli,
n: i32,
m: i32,
length: u32,
vacuum: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::nanotube(n, m, length, vacuum).map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn c20(
cli: &Cli,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::face_spiral(20).map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn twist(
cli: &Cli,
n: u32,
m: u32,
theta: f64,
layers: u32,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure =
xtalforge::build::commensurate_twist(n, m, theta, layers).map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn ribbon(
cli: &Cli,
width: u32,
length: u32,
vacuum: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::nanoribbon(width, length, vacuum).map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
fn ztc_options(samples: usize, cell: f64, isovalue: f64) -> xtalforge::build::ZtcOptions {
xtalforge::build::ZtcOptions {
samples,
cell,
isovalue,
}
}
pub(crate) fn ztc(
cli: &Cli,
samples: usize,
cell: f64,
isovalue: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::build_ztc(&ztc_options(samples, cell, isovalue))
.map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn schwarzite(
cli: &Cli,
samples: usize,
cell: f64,
isovalue: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::build_schwarzite(&ztc_options(samples, cell, isovalue))
.map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn polymer(
cli: &Cli,
smiles: &str,
monomers: u32,
density: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let build = xtalforge::build::build_polymer(
smiles,
monomers,
&xtalforge::build::PolymerOptions { density },
)
.map_err(CliError::core)?;
if out.is_none() && cli.json {
println!(
"{}",
serde_json::json!({
"structure": build.structure,
"steps": build.steps,
})
);
return Ok(());
}
output::emit_structure(cli, &build.structure, out, format)?;
if !cli.quiet && !cli.json {
for step in &build.steps {
println!("step: {step}");
}
}
Ok(())
}
pub(crate) fn c60(
cli: &Cli,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let structure = xtalforge::build::fullerene(60).map_err(CliError::core)?;
output::emit_structure(cli, &structure, out, format)
}
pub(crate) fn hydrogens(
cli: &Cli,
path: &str,
distance: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
use xtalforge::build::hydrogens::{HydrogenOptions, add_hydrogens};
let structure = crate::input::read_structure_path(path).map_err(CliError::core)?;
let filled = add_hydrogens(
&structure,
&HydrogenOptions {
distance,
..Default::default()
},
)
.map_err(CliError::core)?;
output::emit_structure(cli, &filled, out, format)
}
#[allow(clippy::too_many_arguments)] pub(crate) fn carbon(
cli: &Cli,
net: &str,
repeat: [usize; 3],
density: f64,
radius_ratio: f64,
coverage: f64,
seed: u64,
hydrogen: f64,
fluorine: f64,
oxygen: f64,
out: Option<&str>,
format: Option<StructureFormat>,
) -> Result<(), CliError> {
let recipe = xtalforge::build::CarbonRecipe {
net: net.to_owned(),
repeat,
density,
radius_ratio,
coverage,
seed,
hydrogen,
fluorine,
oxygen,
..xtalforge::build::CarbonRecipe::default()
};
let build = xtalforge::build::build_carbon(&recipe).map_err(CliError::core)?;
if cli.json {
println!("{}", json!(build));
return Ok(());
}
println!(
"{} atoms, {:.3} g/cm3, mean C-C {:.3} A, {} piece(s), periodicity {}",
build.structure.sites().len(),
build.density,
build.mean_bond,
build.pieces,
build.periodicity
);
for note in &build.notes {
println!("note: {note}");
}
output::emit_structure(cli, &build.structure, out, format)
}