Run BigDFT and manipulate its output files¶
After the presentation of the BigDFT input files, it now time run actual BigDFT calculations with MyBigDFT. This is made possible via the Job class.
The result of a BigDFT calculation is stored in a logfile using the YAML format (just like the input file). The Logfile class is meant to ease the manipulation of this output data.
The Logfile class¶
As a first example, you can read an already existing logfile via the from_file
method. It only requires the path to an already existing logfile:
[1]:
from mybigdft import Logfile
log = Logfile.from_file("../../../tests/log.yaml")
You can get the input parameters used to run the calculation via the inputparams
attribute:
[2]:
inp = log.inputparams
print(inp)
{}
Here, default parameters were used.
You can also get the initial positions used via the posinp
attribute:
[3]:
pos = log.posinp
print(pos)
2 angstroem
free
N 2.97630782434901e-23 6.87220595204354e-23 0.0107161998748779
N -1.10434491945017e-23 -4.87342174483075e-23 1.10427379608154
The same Posinp
instance is actually stored under the posinp
attribute of the input parameters:
[4]:
assert inp.posinp == log.posinp
The Job class¶
To run a calculation, use the Job
class. It requires the input geometry (given by the input parameters or by the posinp) and generally some input parameters. You may also provide a name to the calculation, so as to set the name for the input and output files. You can also define where to run the calculation by specifying the run_dir
argument.
Finally, the most important thing to know about the Job
class is that it must be used with a context manager. This ensures that the files are created in the correct location and that the BigDFT calculation is run in the same location.
[5]:
from mybigdft import Job
import os
# No need to provide a posinp explicitly, since it is stored in inp
# under the `posinp` attribute.
with Job(inputparams=inp, name="test") as job:
# The calculation must be run in the context manager.
# Here, it uses 6 processors and 3 OpenMP threads.
job.run(nmpi=6, nomp=3, dry_run=True)
# Setting dry_run to True runs bigdft-tool instead of
# bigdft and creates the input files. This is a good way
# of testing the input parameters and positions before
# running the actual BigDFT calculation.
assert os.path.isfile("test.yaml")
assert os.path.isfile("test.xyz")
# A logfile is also created, containing the output of the
# bigdft-tool run. You can check it to get an estimation
# of the memory requirement of the calculation.
assert os.path.isfile("log-test.yaml")
# The input and output files can also be removed from the
# disk. This must be performed in the context manager as
# well, by running the clean method. However, you generally
# do not want to erase your output files after running the
# calculation.
job.clean()
assert not os.path.isfile("log-test.yaml")
assert not os.path.isfile("test.yaml")
assert not os.path.isfile("test.xyz")
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
/Users/maximemoriniere/Documents/bigdft/bigdft/build-1997/install/bin/bigdft-tool --name test -n 6 ...
Note that you can still use job
after exiting the context manager. It is for instance possible to look for some attributes of the output of the bigdft-tool executable, and compare them to those of the base logfile used to initialize the input parameters and geometry:
[6]:
for attr in ["energy", "dipole", "inputparams", "posinp"]:
print(f"{attr}")
print(f" job: {getattr(job.logfile, attr)}")
print(f" log: {getattr(log, attr)}")
energy
job: None
log: -19.884659235401838
dipole
job: None
log: [-0.00051199, -0.00051199, -0.00055711]
inputparams
job: {}
log: {}
posinp
job: 2 angstroem
free
N 2.97630782434901e-23 6.87220595204354e-23 0.0107161998748779
N -1.10434491945017e-23 -4.87342174483075e-23 1.10427379608154
log: 2 angstroem
free
N 2.97630782434901e-23 6.87220595204354e-23 0.0107161998748779
N -1.10434491945017e-23 -4.87342174483075e-23 1.10427379608154
Note that the output of the bigdft-tool do not give the energy nor the dipole of the system: to run the actual bigdft calculation, you must not set dry_run to True
(see below). However, the input parameters and the initial positions are exactly the same as those of the base logfile, as expected!
To run the bigdft executable, you must therefore run the following code:
[7]:
with Job(inputparams=inp, name="test") as job:
assert not job.is_completed
job.run(nmpi=6, nomp=3)
assert job.is_completed
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
mpirun -np 6 /Users/maximemoriniere/Documents/bigdft/bigdft/build-1997/install/bin/bigdft test ...
<BigDFT> log of the run will be written in logfile: ./log-test.yaml
Now, the output of the calculation is the same as the base logfile, up to some numerical noise:
[8]:
for attr in ["energy", "dipole"]:
print(f"{attr}")
print(f" job: {getattr(job.logfile, attr)}")
print(f" log: {getattr(log, attr)}")
energy
job: -19.884659235399404
log: -19.884659235401838
dipole
job: [-0.00051199, -0.00051199, -0.00055711]
log: [-0.00051199, -0.00051199, -0.00055711]
Force the calculation to run¶
The default behaviour of the run
method is to read an already existing logfile instead of actually running the calculation again:
[9]:
with Job(inputparams=inp, name="test") as job:
job.run(nmpi=6, nomp=3)
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
Logfile log-test.yaml already exists!
You can force to run the calculation by setting the force_run
argument of the run
method to True
. This forces to run the calculation even though a logfile already exists.
[10]:
with Job(inputparams=inp, name="test") as job:
# The calculation is forced to run: the previous logfile
# is moved to the logfiles directory (created for the occasion).
job.run(nmpi=6, nomp=3, force_run=True)
# You can remove the logfiles directory as well
job.clean(logfiles_dir=True)
assert not os.path.isdir("logfiles")
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
mpirun -np 6 /Users/maximemoriniere/Documents/bigdft/bigdft/build-1997/install/bin/bigdft test ...
<BigDFT> log of the run will be written in logfile: ./log-test.yaml
<BigDFT> Logfile existing, renamed into: ./logfiles/log-test.17:04:17.065.yaml
What if the run fails?¶
The run or dry_run should both fail if invalid input parameters are caught by the initialization procedure. Here, we take the example of a case where the user wanted to use surface boundary conditions but placed the “.inf” as the cell size along the \(z\) direction (instead of the \(y\) direction). Note that the error message prints the actual error message received when running the bigdft-tool or bigdft executable (depending on the value of dry_run
).
[11]:
from mybigdft import InputParams
inp = InputParams({"posinp": {
"units": "angstroem",
"cell": [40, 40, ".inf"],
"positions": [
{'N': [2.97630782434901e-23, 6.87220595204354e-23, 0.0107161998748779]},
{'N': [-1.10434491945017e-23, -4.87342174483075e-23, 1.10427379608154]},
]
}})
with Job(inputparams=inp, name="test") as job:
job.run(nmpi=6, nomp=3, dry_run=True)
job.clean()
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
/Users/maximemoriniere/Documents/bigdft/bigdft/build-1997/install/bin/bigdft-tool --name test -n 6 ...
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
<ipython-input-11-111fe3f096f8> in <module>()
11
12 with Job(inputparams=inp, name="test") as job:
---> 13 job.run(nmpi=6, nomp=3, dry_run=True)
14 job.clean()
~/Documents/Python/MyBigDFT/mybigdft/job.py in run(self, nmpi, nomp, force_run, dry_run)
453 self.write_input_files()
454 command = self._get_command(nmpi, dry_run)
--> 455 output_msg = self._launch_calculation(command)
456 if dry_run:
457 self._write_bigdft_tool_output(output_msg)
~/Documents/Python/MyBigDFT/mybigdft/job.py in _launch_calculation(command)
644 raise RuntimeError(
645 "The calculation ended with the following error message:{}"
--> 646 .format(error_msg))
647 return out
648
RuntimeError: The calculation ended with the following error message:
abi_symdet: ERROR -
Abs(determinant) for symmetry number 1 is 0 .
For a legitimate symmetry, abs(determinant) must be 1.
Action : check your symmetry operations (symrel) in input file.
Note: The following floating-point exceptions are signalling: IEEE_INVALID_FLAG
STOP MPIFAKE: mpi_error_string
[12]:
# Let us clean the present directory again:
with job as job:
job.clean()
/Users/maximemoriniere/Documents/Python/MyBigDFT/doc/source/notebooks
It might happen that the error messages are not properly sent or received, depending on your platform (for instance with Bash on Ubuntu on Windows, under Windows 10). This might break this particulat behaviour, but you should still get an error. The error will however differ if running in dry mode or not.