#!/usr/bin/env perl
use warnings;
use strict;
use Test::More;
use File::Slurp;
use Cwd;
use Test::Warn;
use Test::Output;
use Test::Fatal qw(lives_ok dies_ok);
use Test::Moose;
use Math::Vector::Real;
use HackaMol::Atom;
use HackaMol::AtomGroup; # v0.001;#To test for version availability
use Scalar::Util qw(refaddr);
my @attributes = qw(
atoms
);
my @methods = qw(
bin_atoms dipole COM COZ
dipole_moment total_charge
count_unique_atoms
bin_atoms_name all_atoms
push_atoms get_atoms delete_atoms
count_atoms clear_atoms
rotate translate print_xyz
);
map has_attribute_ok( 'HackaMol::AtomGroup', $_ ), @attributes;
map can_ok ( 'HackaMol::AtomGroup', $_ ), @methods;
my $group;
lives_ok {
$group = HackaMol::AtomGroup->new();
}
'Test creation of an group';
my $atom1 = HackaMol::Atom->new(
name => 'O',
charges => [ -0.80, -0.82, -0.834 ],
coords => [ V(2.05274, 0.01959, -0.07701) ],
Z => 8
);
my $atom2 = HackaMol::Atom->new(
name => 'H',
charges => [0.4,0.41,0.417],
coords => [ V( 1.08388, 0.02164, -0.12303 ) ],
Z => 1
);
my $atom3 = HackaMol::Atom->new(
name => 'H',
charges => [0.4,0.41,0.417],
coords => [ V( 2.33092, 0.06098, -1.00332 ) ],
Z => 1
);
$group->push_atoms($_) foreach ($atom1, $atom2, $atom3);
$group->do_forall('copy_ref_from_t1_through_t2','coords', 0, 2);
is($group->count_atoms, 3, 'atom count');
foreach my $at ($group->all_atoms){
cmp_ok(refaddr($at->get_coords(0)) , '==' , refaddr($at->get_coords($_)),
"do_forall(copy_ref_from_t1_through_t2, coords, 0 , 2): $_") foreach 1 .. 2;
}
my @dipole_moments = qw(2.293 2.350 2.390);
$group->do_forall('t',0);
foreach my $at ($group->all_atoms){
is($at->t, 0, "\$atom->t(0) for each atom: group->do_for_all");
}
$group->gt(1);
foreach my $at ($group->all_atoms){
is($at->t, 1, "\$atom->t(1) for each atom: group->gt");
}
foreach my $t (0 .. 2){
$group->gt($t);
cmp_ok(abs($group->dipole_moment-$dipole_moments[$t]), '<' , 0.001, "dipole moment at t=$t");
}
my $atom4 = HackaMol::Atom->new(
name => 'H',
charges => [0.0],
coords => [ V( 0,0,0 ) ],
Z => 1
);
my $atom5 = HackaMol::Atom->new(
name => 'H',
charges => [0.0],
coords => [ V( 1,0,0 ) ],
Z => 1
);
my $atom6 = HackaMol::Atom->new(
name => 'H',
charges => [0.0],
coords => [ V( 2,0,0 ) ],
Z => 1
);
$group->clear_atoms;
is($group->count_atoms, 0, 'atom clear atom count');
$group->push_atoms($atom4);
is($group->count_atoms, 1, 'atom atom count 1');
$group->push_atoms($atom5);
is($group->count_atoms, 2, 'atom atom count 2');
is_deeply($group->COM, V (0.5,0,0), 'Center of mass');
is_deeply($group->COZ, V (0.5,0,0), 'Center of Z');
$group->push_atoms($atom6);
is($group->count_atoms, 3, 'atom atom count 3');
is_deeply($group->COM, V (1,0,0), 'Center of mass');
is_deeply($group->COZ, V (1,0,0), 'Center of Z');
$group->clear_atoms;
is_deeply($group->COM, V (0), 'Center of mass V (0) no atoms');
is_deeply($group->COZ, V (0), 'Center of Z V (0) no atoms');
is_deeply($group->dipole, V (0), 'Dipole V (0) no atoms');
my @atoms = map{HackaMol::Atom->new(Z=>1, coords=> [V($_, $_, $_)])} 1 .. 10;
$group->push_atoms(@atoms);
is_deeply($group->COM, V(5.5,5.5,5.5),
'Center of mass 10 atoms [1,1,1]...[10,10,10]');
is_deeply($group->COZ, V(5.5,5.5,5.5),
'Center of Z 10 atoms [1,1,1]...[10,10,10]');
warning_is { $group->dipole }
"build_dipole> mismatch number of coords and charges. all defined?",
"carp warning> mismatch number of coords and charges. ";
$group->do_forall('set_charges', $group->get_atoms(0)->t, 0);
warning_is { $group->do_forall('set_charges') }
"doing nothing for all",
"carp warning> doing nothing for all ";
is_deeply($group->dipole, V(0,0,0),
'dipole (0,0,0) atoms [1,1,1]...[10,10,10]');
#cmp_ok(abs($group->Rg-4.97493), '<', 0.0001, "Rg for the ten atoms, double check" );
is($group->bin_atoms_name, "H10", "bin_atoms name is H10");
$group->delete_atoms(0) foreach 0 .. 4 ;
is_deeply($group->COM, V(8,8,8),
'center of mass delete first 5 of 10 atoms [1,1,1]...[10,10,10]');
is($group->bin_atoms_name, "H5", "bin_atoms name is H5");
$group->clear_atoms;
$group->push_atoms($atom1);
$group->push_atoms($atom2);
$group->push_atoms($atom3);
is($group->count_unique_atoms, 2, 'unique atoms in water is 2');
is($group->bin_atoms_name, 'OH2', 'water named OH2');
$group->push_atoms($atom1);
is($group->count_unique_atoms, 2, 'push O1 again, unique atoms still 2');
is($group->bin_atoms_name, 'O2H2', 'now named O2H2');
cmp_ok (abs(-0.834-$group->total_charge), '<', 1E-7, 'total charge' );
cmp_ok (abs(34.01468-$group->total_mass), '<', 1E-7, 'total mass' );
cmp_ok ($group->total_Z, '==', 18, 'total Z' );
#we have two copies of atom1 in the molecule
my $xyz = $atom1->xyz;
$group->translate(V(1,0,0));
is_deeply($atom1->xyz-$xyz, V(2,0,0), "two copies of an atom gets double the intended translations:beware ");
$group->delete_atoms(3); #delete the copy of atom 1
my $COM = $group->COM;
$group->translate(V(1,0,0));
is_deeply($group->COM-$COM, V(1,0,0), "COM after translation ");
dies_ok{$group->translate} "translate dies with no args";
dies_ok{$group->rotate} "rotate dies with no args";
dies_ok{$group->rotate(V(1,0,0))} "rotate dies with 1 args";
dies_ok{$group->rotate(V(1,0,0), 30)} "rotate dies with 2 args";
$group->gt(0);
$COM = $group->COM;
my $xyz1 =
'3
O 2.052740 0.019590 -0.077010
H 1.083880 0.021640 -0.123030
H 2.330920 0.060980 -1.003320
';
my $pdb =
'HETATM 0 O ALA 0 2.053 0.020 -0.077 1.00 20.00 O
HETATM 0 H ALA 0 1.084 0.022 -0.123 1.00 20.00 H
HETATM 0 H ALA 0 2.331 0.061 -1.003 1.00 20.00 H
';
#print_pdb tests
stdout_is(sub{$group->print_pdb},$pdb,"print_pdb no arg");
#print_xyz tests
stdout_is(sub{$group->print_xyz},$xyz1,"print_xyz no arg");
my $dir = getcwd;
my $tfl = $dir."/t/lib/tmp.xyz";
unlink($tfl);
is(-e $tfl, undef, 'no xyz_file');
my $fh = $group->print_xyz($tfl);
is(-e $tfl, 1, 'xyz_file written');
$group->print_xyz($fh);
$fh->close;
my $tmp = read_file($tfl);
is($tmp,$xyz1.$xyz1,"printed twice");
warning_is { $group->print_xyz($tfl) }
"overwrite $tfl",
"carp warning> overwrite xyz ";
my $xyz2 =
'3
O 2.052740 0.024451 -0.185812
H 1.083880 0.022401 -0.139792
H 2.330920 -0.016939 0.740498
';
$group->rotate(V(1,0,0), 180, $COM,1);
$group->gt(1);
#some rotation tests
cmp_ok(abs($group->COM-$COM), '<', 1E-7, "COM after rotation ");
stdout_is(sub{$group->print_xyz},$xyz2,"print_xyz after rotation 180");
$group->rotate(V(1,0,0), 180, $COM);
stdout_is(sub{$group->print_xyz},$xyz1,"print_xyz after rotation 180 again");
$group->clear_atoms;
cmp_ok (abs(0-$group->total_charge), '<', 1E-7, 'cleared total charge' );
cmp_ok (abs(0-$group->total_mass), '<', 1E-7, 'cleared total mass' );
cmp_ok ($group->total_Z, '==', 0, 'cleared total Z' );
my ($bin_hr,$z_hr) = $group->bin_atoms;
is_deeply($bin_hr,{}, "empty bin_hr");
is_deeply($z_hr,{}, "empty z_hr");
unlink($tfl);
done_testing();