use crate::restraint::AtomRestraint;
use molrs::types::F;
#[derive(Debug, Clone, Copy)]
pub struct AbovePlaneRestraint {
pub normal: [F; 3],
pub distance: F,
}
impl AbovePlaneRestraint {
pub fn new(normal: [F; 3], distance: F) -> Self {
Self { normal, distance }
}
}
impl AtomRestraint for AbovePlaneRestraint {
fn f(&self, pos: &[F; 3], scale: F, _scale2: F) -> F {
let n = self.normal;
let w = n[0] * pos[0] + n[1] * pos[1] + n[2] * pos[2] - self.distance;
let a1 = w.min(0.0);
scale * a1 * a1
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let n = self.normal;
let d = n[0] * pos[0] + n[1] * pos[1] + n[2] * pos[2] - self.distance;
if d < 0.0 {
let ds = scale * d;
g[0] += 2.0 * n[0] * ds;
g[1] += 2.0 * n[1] * ds;
g[2] += 2.0 * n[2] * ds;
}
self.f(pos, scale, scale2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BelowPlaneRestraint {
pub normal: [F; 3],
pub distance: F,
}
impl BelowPlaneRestraint {
pub fn new(normal: [F; 3], distance: F) -> Self {
Self { normal, distance }
}
}
impl AtomRestraint for BelowPlaneRestraint {
fn f(&self, pos: &[F; 3], scale: F, _scale2: F) -> F {
let n = self.normal;
let w = n[0] * pos[0] + n[1] * pos[1] + n[2] * pos[2] - self.distance;
let a1 = w.max(0.0);
scale * a1 * a1
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let n = self.normal;
let d = n[0] * pos[0] + n[1] * pos[1] + n[2] * pos[2] - self.distance;
if d > 0.0 {
let ds = scale * d;
g[0] += 2.0 * n[0] * ds;
g[1] += 2.0 * n[1] * ds;
g[2] += 2.0 * n[2] * ds;
}
self.f(pos, scale, scale2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct InsideCylinderRestraint {
pub center: [F; 3],
pub axis: [F; 3],
pub radius: F,
pub length: F,
}
impl InsideCylinderRestraint {
pub fn new(center: [F; 3], axis: [F; 3], radius: F, length: F) -> Self {
debug_assert!(
axis[0] != 0.0 || axis[1] != 0.0 || axis[2] != 0.0,
"cylinder axis must be a non-zero direction"
);
Self {
center,
axis,
radius,
length,
}
}
}
impl AtomRestraint for InsideCylinderRestraint {
fn f(&self, pos: &[F; 3], _scale: F, scale2: F) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let c = self.center;
let ax = self.axis;
let (a1, a2, a3) = (x - c[0], y - c[1], z - c[2]);
let vnorm = (ax[0].powi(2) + ax[1].powi(2) + ax[2].powi(2)).sqrt();
let (vv1, vv2, vv3) = (ax[0] / vnorm, ax[1] / vnorm, ax[2] / vnorm);
let w = vv1 * a1 + vv2 * a2 + vv3 * a3;
let d = (a1 - vv1 * w).powi(2) + (a2 - vv2 * w).powi(2) + (a3 - vv3 * w).powi(2);
scale2
* ((-w).max(0.0).powi(2)
+ (w - self.length).max(0.0).powi(2)
+ (d - self.radius.powi(2)).max(0.0).powi(2))
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let c = self.center;
let ax = self.axis;
let (a1, a2, a3) = (x - c[0], y - c[1], z - c[2]);
let vnorm = (ax[0].powi(2) + ax[1].powi(2) + ax[2].powi(2)).sqrt();
let (vv1, vv2, vv3) = (ax[0] / vnorm, ax[1] / vnorm, ax[2] / vnorm);
let w = vv1 * a1 + vv2 * a2 + vv3 * a3;
let d = (a1 - vv1 * w).powi(2) + (a2 - vv2 * w).powi(2) + (a3 - vv3 * w).powi(2);
let len = self.length;
let r2 = self.radius.powi(2);
let rg0 = scale2
* (-2.0 * (-w).max(0.0) * vv1
+ 2.0 * (w - len).max(0.0) * vv1
+ 2.0
* (d - r2).max(0.0)
* (2.0 * (a1 - vv1 * w) * (1.0 - vv1.powi(2))
+ 2.0 * (a2 - vv2 * w) * (-vv2 * vv1)
+ 2.0 * (a3 - vv3 * w) * (-vv3 * vv1)));
let rg1 = scale2
* (-2.0 * (-w).max(0.0) * vv2
+ 2.0 * (w - len).max(0.0) * vv2
+ 2.0
* (d - r2).max(0.0)
* (2.0 * (a1 - vv1 * w) * (-vv1 * vv2)
+ 2.0 * (a2 - vv2 * w) * (1.0 - vv2.powi(2))
+ 2.0 * (a3 - vv3 * w) * (-vv3 * vv2)));
let rg2 = scale2
* (-2.0 * (-w).max(0.0) * vv3
+ 2.0 * (w - len).max(0.0) * vv3
+ 2.0
* (d - r2).max(0.0)
* (2.0 * (a1 - vv1 * w) * (-vv1 * vv3)
+ 2.0 * (a2 - vv2 * w) * (-vv2 * vv3)
+ 2.0 * (a3 - vv3 * w) * (1.0 - vv3.powi(2))));
g[0] += rg0;
g[1] += rg1;
g[2] += rg2;
self.f(pos, scale, scale2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct OutsideCylinderRestraint {
pub center: [F; 3],
pub axis: [F; 3],
pub radius: F,
pub length: F,
}
impl OutsideCylinderRestraint {
pub fn new(center: [F; 3], axis: [F; 3], radius: F, length: F) -> Self {
debug_assert!(
axis[0] != 0.0 || axis[1] != 0.0 || axis[2] != 0.0,
"cylinder axis must be a non-zero direction"
);
Self {
center,
axis,
radius,
length,
}
}
}
impl AtomRestraint for OutsideCylinderRestraint {
fn f(&self, pos: &[F; 3], _scale: F, scale2: F) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let c = self.center;
let ax = self.axis;
let (a1, a2, a3) = (x - c[0], y - c[1], z - c[2]);
let vnorm = (ax[0].powi(2) + ax[1].powi(2) + ax[2].powi(2)).sqrt();
let (vv1, vv2, vv3) = (ax[0] / vnorm, ax[1] / vnorm, ax[2] / vnorm);
let w = vv1 * a1 + vv2 * a2 + vv3 * a3;
let d = (a1 - vv1 * w).powi(2) + (a2 - vv2 * w).powi(2) + (a3 - vv3 * w).powi(2);
scale2
* ((-w).min(0.0).powi(2)
* (w - self.length).min(0.0).powi(2)
* (d - self.radius.powi(2)).min(0.0).powi(2))
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let c = self.center;
let ax = self.axis;
let (a1, a2, a3) = (x - c[0], y - c[1], z - c[2]);
let vnorm = (ax[0].powi(2) + ax[1].powi(2) + ax[2].powi(2)).sqrt();
let (vv1, vv2, vv3) = (ax[0] / vnorm, ax[1] / vnorm, ax[2] / vnorm);
let w = vv1 * a1 + vv2 * a2 + vv3 * a3;
let d = (a1 - vv1 * w).powi(2) + (a2 - vv2 * w).powi(2) + (a3 - vv3 * w).powi(2);
let len = self.length;
let r2 = self.radius.powi(2);
let fra = (-w).min(0.0).powi(2);
let frb = (w - len).min(0.0).powi(2);
let frc = (d - r2).min(0.0).powi(2);
let frab = fra * frb;
let frac = fra * frc;
let frbc = frb * frc;
let dfra0 = -2.0 * (-w).min(0.0) * vv1;
let dfrb0 = 2.0 * (w - len).min(0.0) * vv1;
let dfrc0 = 2.0
* (d - r2).min(0.0)
* (2.0 * (a1 - vv1 * w) * (1.0 - vv1.powi(2))
+ 2.0 * (a2 - vv2 * w) * (-vv2 * vv1)
+ 2.0 * (a3 - vv3 * w) * (-vv3 * vv1));
let dfra1 = -2.0 * (-w).min(0.0) * vv2;
let dfrb1 = 2.0 * (w - len).min(0.0) * vv2;
let dfrc1 = 2.0
* (d - r2).min(0.0)
* (2.0 * (a1 - vv1 * w) * (-vv1 * vv2)
+ 2.0 * (a2 - vv2 * w) * (1.0 - vv2.powi(2))
+ 2.0 * (a3 - vv3 * w) * (-vv3 * vv2));
let dfra2 = -2.0 * (-w).min(0.0) * vv3;
let dfrb2 = 2.0 * (w - len).min(0.0) * vv3;
let dfrc2 = 2.0
* (d - r2).min(0.0)
* (2.0 * (a1 - vv1 * w) * (-vv1 * vv3)
+ 2.0 * (a2 - vv2 * w) * (-vv2 * vv3)
+ 2.0 * (a3 - vv3 * w) * (1.0 - vv3.powi(2)));
g[0] += scale2 * (dfra0 * frbc + dfrb0 * frac + dfrc0 * frab);
g[1] += scale2 * (dfra1 * frbc + dfrb1 * frac + dfrc1 * frab);
g[2] += scale2 * (dfra2 * frbc + dfrb2 * frac + dfrc2 * frab);
self.f(pos, scale, scale2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct AboveGaussianRestraint {
pub cx: F,
pub cy: F,
pub sx: F,
pub sy: F,
pub z0: F,
pub height: F,
}
impl AboveGaussianRestraint {
pub fn new(cx: F, cy: F, sx: F, sy: F, z0: F, height: F) -> Self {
Self {
cx,
cy,
sx,
sy,
z0,
height,
}
}
}
impl AtomRestraint for AboveGaussianRestraint {
fn f(&self, pos: &[F; 3], scale: F, _scale2: F) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let e1 = -(x - self.cx).powi(2) / (2.0 * self.sx.powi(2));
let e2 = -(y - self.cy).powi(2) / (2.0 * self.sy.powi(2));
let w = if e1 + e2 <= -50.0 {
-(z - self.z0)
} else {
self.height * (e1 + e2).exp() - (z - self.z0)
};
let a1 = w.max(0.0);
scale * a1 * a1
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let e1 = -(x - self.cx).powi(2) / (2.0 * self.sx.powi(2));
let e2 = -(y - self.cy).powi(2) / (2.0 * self.sy.powi(2));
let d_raw = if e1 + e2 <= -50.0 {
-(z - self.z0)
} else {
self.height * (e1 + e2).exp() - (z - self.z0)
};
if d_raw > 0.0 {
let d = scale * d_raw;
g[0] += -2.0 * d * (x - self.cx) * (d + (z - self.z0)) / self.sx.powi(2);
g[1] += -2.0 * d * (y - self.cy) * (d + (z - self.z0)) / self.sy.powi(2);
g[2] += -2.0 * d;
}
self.f(pos, scale, scale2)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BelowGaussianRestraint {
pub cx: F,
pub cy: F,
pub sx: F,
pub sy: F,
pub z0: F,
pub height: F,
}
impl BelowGaussianRestraint {
pub fn new(cx: F, cy: F, sx: F, sy: F, z0: F, height: F) -> Self {
Self {
cx,
cy,
sx,
sy,
z0,
height,
}
}
}
impl AtomRestraint for BelowGaussianRestraint {
fn f(&self, pos: &[F; 3], scale: F, _scale2: F) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let e1 = -(x - self.cx).powi(2) / (2.0 * self.sx.powi(2));
let e2 = -(y - self.cy).powi(2) / (2.0 * self.sy.powi(2));
let w = if e1 + e2 <= -50.0 {
-(z - self.z0)
} else {
self.height * (e1 + e2).exp() - (z - self.z0)
};
let a1 = w.min(0.0);
scale * a1 * a1
}
fn fg(&self, pos: &[F; 3], scale: F, scale2: F, g: &mut [F; 3]) -> F {
let (x, y, z) = (pos[0], pos[1], pos[2]);
let e1 = -(x - self.cx).powi(2) / (2.0 * self.sx.powi(2));
let e2 = -(y - self.cy).powi(2) / (2.0 * self.sy.powi(2));
let d_raw = if e1 + e2 <= -50.0 {
-(z - self.z0)
} else {
self.height * (e1 + e2).exp() - (z - self.z0)
};
if d_raw < 0.0 {
let d = scale * d_raw;
g[0] += -2.0 * d * (x - self.cx) * (d + (z - self.z0)) / self.sx.powi(2);
g[1] += -2.0 * d * (y - self.cy) * (d + (z - self.z0)) / self.sy.powi(2);
g[2] += -2.0 * d;
}
self.f(pos, scale, scale2)
}
}