ESjob class - pt. 2 ePS job creation & run jobs

12/02/22

2nd demo for ESjob class, including host connection and job writing.

For basic electronic structure file IO and creation of ePS jobs based on inputs (pt 1) see epsman_ESjob-class_demo_120222.ipynb.

  • Currently tested for Gamess and Molden IO only.

  • Uses CCLIB on the backend, so should be easily extendable to other CCLIB-supported cases.

[ ]:

Imports

[1]:
# Import main package
# import epsman as em

# For ePS job creation with electronic structure handling, use elecStructure.ESjob
from epsman.elecStructure.ESjob import ESjob
[2]:
# For testing, set the module path and test file.
import inspect
from pathlib import Path

modDir = Path(inspect.getfile(ESjob)).parent
testFilePath = modDir/'fileTest'  # Default module test files, these are included with Github repo.

Class creation with host

Minimally this needs an IP, which can be a host name for known hosts. The user will be prompted for any other missing info when trying to connect.

Note that the host machine can be the same as the local machine. Simply pass host=<somename> along with IP = 'localhost' in this case (a different hostname is required in this case since the local machine already sets localhost as a key name - this might change in future).

TODO: update script and related paths to repo?

[3]:
# For ESjob class basic file handling for Gamess Log files is implemented.
job = ESjob(fileName = 'xe_SPKrATZP_rel.log', fileBase = testFilePath,
            IP = 'ePS-VM')
#             host = 'ePS-VM', IP = 'ePS-VM')
Set host = None
Set user = None
Set IP = ePS-VM
Set password = None
Set mol = None
Set orb = None
Set batch = None
Set jobNote = None
Set elecStructure = None
Set genFile = None
Set jobSettings = None
Set runScript = None
Skipping setJobPaths() until job settings defined, run setJob() to set.
Set elecStructure = xe_SPKrATZP_rel.log

Set input file as /home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.log, use self.setFiles to change.
Set output file as /home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.molden, use self.setMoldenFile to override.

*** Read file /home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.log with CCLIB, data set to self.data.
Read 1 atoms and 68 MOs
***Failed to get https://epolyscat.droppages.com/SymmetryLabels, setting symmetry labels from local file.
***Warning: inconsistent dim mapping, some Gamess or ePS dims unmapped. Check PD table for details.
***Failed to get https://epolyscat.droppages.com/SymmetryLabels, setting symmetry labels from local file.
*** Set orbPD data to self.orbPD, set group data to self.orbGrps

Found input file Point Group: {'Name': 'DNH', 'NAXIS': '8', 'ORDER': '32'}.
Mapped PGs: Gamess (DNH, 8)  > ePS (DAh) dim mapping.
Found 68 orbitals, in 45 groups.
Found 7 orb symmetries: ['A1g' 'A2u' 'E1u' 'E1g' 'E2g' 'E3u' 'E2u']
Assigned 54 electrons to 27 orbitals/19 orbital groups.

Occupied orbitals table:
/home/eps/.conda/envs/epsman-demo/lib/python3.7/site-packages/numpy/core/numeric.py:2378: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  return bool(asarray(a1 == a2).all())
syms Occ OccN degen OrbGrpOcc Gamess ePS
E iOrbGrp OrbN
-34724.875681 1 1 A1g True 2 1 2 A1G SG
-5506.008795 2 2 A1g True 2 1 2 A1G SG
-4937.337107 3 3 A2u True 2 1 2 A2U A2U
4 4 E1u True 2 2 4 E1U PU
5 E1u True 2 2 4 E1U PU
-1169.749415 5 6 A1g True 2 1 2 A1G SG
-981.237103 6 7 A2u True 2 1 2 A2U A2U
7 8 E1u True 2 2 4 E1U PU
9 E1u True 2 2 4 E1U PU
-700.203360 9 10 E1g True 2 2 4 E1G PG
11 E1g True 2 2 4 E1G PG
8 12 A1g True 2 1 2 A1G SG
10 13 E2g True 2 2 4 E2G DG
14 E2g True 2 2 4 E2G DG
-229.247756 11 15 A1g True 2 1 2 A1G SG
-166.824838 12 16 A2u True 2 1 2 A2U A2U
13 17 E1u True 2 2 4 E1U PU
18 E1u True 2 2 4 E1U PU
-72.521062 14 19 A1g True 2 1 2 A1G SG
15 20 E1g True 2 2 4 E1G PG
21 E1g True 2 2 4 E1G PG
16 22 E2g True 2 2 4 E2G DG
23 E2g True 2 2 4 E2G DG
-27.472614 17 24 A1g True 2 1 2 A1G SG
-12.421997 18 25 A2u True 2 1 2 A2U A2U
19 26 E1u True 2 2 4 E1U PU
27 E1u True 2 2 4 E1U PU
[4]:
# Connect to host
job.initConnection()
Connecting to machine: None at ePS-VM
User name for machine?  eps
Password for machine?  ·········
Testing connection...
eps-VM
Connected OK
Command exited with status 0.
=== stdout ===
eps-VM

(no stderr)


Setting host dir tree.
Found multiple ePS directories, please select working dir:
0: ePS
1: ePS_minimal_build_v3885d87.txt
2: ePS_v3885d87_VM_build_notes
List item #:  0
Set remote wrkdir: /home/eps/ePS

***Default paths set
{'IP': 'ePS-VM',
 'condaEnv': 'base',
 'condaPath': PosixPath('/home/eps/anaconda3/bin/activate'),
 'ePSpath': PosixPath('/opt/ePolyScat.E3/bin/ePolyScat'),
 'home': PosixPath('/home/eps'),
 'host': 'eps-VM',
 'jobComplete': PosixPath('/home/eps/ePS/jobs/completed'),
 'jobPath': PosixPath('/home/eps/ePS/jobs'),
 'repoScpPath': PosixPath('/home/eps/python/epsman/repo'),
 'scpFile': PosixPath('/home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic_noDefaults.sh'),
 'scpdir': PosixPath('/home/eps/github/epsman/epsman/shell'),
 'webDir': PosixPath('/home/eps/github/ePSdata'),
 'webScpPath': PosixPath('/home/eps/python/epsman/web'),
 'wrkdir': PosixPath('/home/eps/ePS')}

***Checking bin dirs
***ePolyScat bin not found
***Anaconda bin not found
[5]:
# All host definitions are set in job.hostDefn
job.hostDefn
[5]:
{'localhost': {'host': 'jake',
  'IP': '127.0.0.1',
  'home': PosixPath('/home/eps'),
  'wrkdir': PosixPath('/home/eps/ePS/epsman-demo/ESclasses'),
  'webDir': PosixPath('/home/eps/github/ePSdata'),
  'elecDir': PosixPath('/home/eps/github/epsman/epsman/elecStructure/fileTest'),
  'elecFile': PosixPath('/home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.log'),
  'localSettings': PosixPath('/home/eps/python/epsman/localSettings')},
 'eps-VM': {'host': 'eps-VM',
  'IP': 'ePS-VM',
  'home': PosixPath('/home/eps'),
  'wrkdir': PosixPath('/home/eps/ePS'),
  'scpdir': PosixPath('/home/eps/github/epsman/epsman/shell'),
  'scpFile': PosixPath('/home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic_noDefaults.sh'),
  'jobPath': PosixPath('/home/eps/ePS/jobs'),
  'jobComplete': PosixPath('/home/eps/ePS/jobs/completed'),
  'ePSpath': PosixPath('/opt/ePolyScat.E3/bin/ePolyScat'),
  'condaPath': PosixPath('/home/eps/anaconda3/bin/activate'),
  'condaEnv': 'base',
  'repoScpPath': PosixPath('/home/eps/python/epsman/repo'),
  'webScpPath': PosixPath('/home/eps/python/epsman/web'),
  'webDir': PosixPath('/home/eps/github/ePSdata')}}
[6]:
# The host name is set in self.host
job.host
[6]:
'eps-VM'
[7]:
# All settings can be overwritten by passing a Path
from pathlib import Path

# Change script dir from default
job.hostDefn[job.host]['scpdir'] = Path('~/github/epsman/shell')

Notes on paths

Currently a bit of a mess…

Default/base paths

The default paths are set as follows:

On local machine in epsJob.__init__:

self.hostDefn = {
    'localhost':{'host':socket.gethostname(),
        'IP':'127.0.0.1',
        'home':Path.home(),
        'wrkdir':Path.cwd(),
        'webDir':Path(Path.home(), 'github/ePSdata')}
    }

Hence local files default to the current working directory.

On remote, at connection, in epsJob.initConnection():

# Build dir list if not already set
if 'home' not in self.hostDefn[self.host].keys():
    print('\n\nSetting host dir tree.')

    if home is None:
        self.hostDefn[self.host]['home'] = Path(self.c.run('echo ~', hide = True).stdout.strip())
    else:
        self.hostDefn[self.host]['home'] = Path(home)

    # testwrkdir = self.c.run('ls -d eP*', hide = True).stdout.split()
    testwrkdir = self.c.run(f'cd {home}; ls -d eP*', hide = True, warn = True).stdout.split()

    if len(testwrkdir) > 1:
        print('Found multiple ePS directories, please select working dir:')
        # print(testwrkdir)
        for n, item in enumerate(testwrkdir):
            print(f'{n}: {item}')

        N = int(input('List item #: '))
        self.hostDefn[self.host]['wrkdir'] = Path(self.hostDefn[self.host]['home'], testwrkdir[N])
    elif testwrkdir:
        self.hostDefn[self.host]['wrkdir'] = Path(self.hostDefn[self.host]['home'], testwrkdir[0])
    else:
        print('No ePS* subdirs found, setting work dir as home dir.')
        self.hostDefn[self.host]['wrkdir'] = Path(self.hostDefn[self.host]['home'])

    print('Set remote wrkdir: ' + self.hostDefn[self.host]['wrkdir'].as_posix())

    # Set additional default paths for host.
    self.setPaths()

This will set self.hostDefn[self.host]['wrkdir'] to an ePS directory if found, or to the user home directory. Or, pass home='homeDir' when running initConnection() to override.

To reset the wrkdir and propage, after connection, use self.setWrkDir(wrkdir='newWorkingDir').

Other default paths are set by self.setPaths(), building from the currently set wrkdir.

TODO:

  • Update self.setPaths(), has some redundant stuff currently (23/02/22).

  • Propagation from self.setWrkDir not complete?

Job paths

These cannot be set until a job has been initalised via self.setJob(). From this, self.setJobPaths() builds a set of job-specific dirs building from self.hostDefn[host]['wrkdir'].

This uses the following schema:

self.hostDefn[host]['systemDir'] = Path(self.hostDefn[host]['wrkdir'], self.mol)
self.hostDefn[host]['elecDir'] = Path(self.hostDefn[host]['systemDir'], 'electronic_structure')
self.hostDefn[host]['genDir'] = Path(self.hostDefn[host]['systemDir'], 'generators')
self.hostDefn[host]['genFile'] = Path(self.hostDefn[host]['genDir'], self.genFile)
# self.hostDefn[host]['jobRoot'] = Path(self.hostDefn[host]['systemDir'], self.genFile.stem)
if self.genFile is not None:
    # self.hostDefn[host]['jobRoot'] = Path(self.hostDefn[host]['systemDir'], Path(self.genFile.stem).stem) # This form will work for X.Y.conf and X.conf styles.
    self.hostDefn[host]['jobRoot'] = Path(self.hostDefn[host]['systemDir'], self.batch) # Just use mol/batch/orb to match dir tree creation?

# self.hostDefn[host]['jobRoot'] = Path(self.hostDefn[host]['systemDir'], self.batch)  # Use job type (batch) here
self.hostDefn[host]['jobDir'] = Path(self.hostDefn[host]['jobRoot'], self.orb)  # Definition here to match shell script. Possibly a bit redundant, but allows for multiple orbs per base job settings.

self.hostDefn[host]['webSystemDir'] = Path(self.hostDefn[host]['webDir'], 'source', self.mol)

Hence

  • <wrkdir>/mol is the base job directory, systemDir.

  • Generator files use systemDir/generators.

  • Electronic structure files systemDir/electronic_structure

  • subdirs per job as systemDir/batch/orbital.

Note that self.setJobPaths() can be run directly, and is also called by self.setGenFile().

Build ePS job

With a host set, the full ePolyScat build process should complete.

[8]:
job.buildePSjob(channel = 18)
*** Set ionization from orbital/channel 18.
Updated orb table...

Occupied orbitals by group:
E OrbN syms Occ OccN degen OrbGrpOcc Gamess ePS OrbGrpOccFinal
iOrbGrp
1 -34724.875681 1 A1g True 2 1 2 A1G SG 2
2 -5506.008795 2 A1g True 2 1 2 A1G SG 2
3 -4937.337107 3 A2u True 2 1 2 A2U A2U 2
4 -4937.337107 4 E1u True 2 2 4 E1U PU 4
5 -1169.749415 6 A1g True 2 1 2 A1G SG 2
6 -981.237103 7 A2u True 2 1 2 A2U A2U 2
7 -981.237103 8 E1u True 2 2 4 E1U PU 4
9 -700.203360 10 E1g True 2 2 4 E1G PG 4
8 -700.203360 12 A1g True 2 1 2 A1G SG 2
10 -700.203360 13 E2g True 2 2 4 E2G DG 4
11 -229.247756 15 A1g True 2 1 2 A1G SG 2
12 -166.824838 16 A2u True 2 1 2 A2U A2U 2
13 -166.824838 17 E1u True 2 2 4 E1U PU 4
14 -72.521062 19 A1g True 2 1 2 A1G SG 2
15 -72.521062 20 E1g True 2 2 4 E1G PG 4
16 -72.521062 22 E2g True 2 2 4 E2G DG 4
17 -27.472614 24 A1g True 2 1 2 A1G SG 2
18 -12.421997 25 A2u True 2 1 2 A2U A2U 1
19 -12.421997 26 E1u True 2 2 4 E1U PU 4

*** Building job mol, orb18 (A2U/DAh), batch: batch
Set mol = mol
Set orb = orb18_A2U
Set batch = batch
Generator file set: mol.batch.orb18_A2U.conf

*** Job paths set in self.hostDefn['eps-VM']:

{'IP': 'ePS-VM',
 'condaEnv': 'base',
 'condaPath': PosixPath('/home/eps/anaconda3/bin/activate'),
 'ePSpath': PosixPath('/opt/ePolyScat.E3/bin/ePolyScat'),
 'elecDir': PosixPath('/home/eps/ePS/mol/electronic_structure'),
 'genDir': PosixPath('/home/eps/ePS/mol/generators'),
 'genFile': PosixPath('/home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf'),
 'home': PosixPath('/home/eps'),
 'host': 'eps-VM',
 'jobComplete': PosixPath('/home/eps/ePS/jobs/completed'),
 'jobDir': PosixPath('/home/eps/ePS/mol/batch/orb18_A2U'),
 'jobPath': PosixPath('/home/eps/ePS/jobs'),
 'jobRoot': PosixPath('/home/eps/ePS/mol/batch'),
 'repoScpPath': PosixPath('/home/eps/python/epsman/repo'),
 'scpFile': PosixPath('/home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic_noDefaults.sh'),
 'scpdir': PosixPath('~/github/epsman/shell'),
 'systemDir': PosixPath('/home/eps/ePS/mol'),
 'webDir': PosixPath('/home/eps/github/ePSdata'),
 'webScpPath': PosixPath('/home/eps/python/epsman/web'),
 'webSystemDir': PosixPath('/home/eps/github/ePSdata/source/mol'),
 'wrkdir': PosixPath('/home/eps/ePS')}
self.symList not set, running for defaults (all symmetry species).
Set self.ePSglobals for global job settings.
{'LMax': 30, 'FegeEng': 12.422}
Written local job conf file (working dir): /home/eps/ePS/epsman-demo/ESclasses/mol.batch.orb18_A2U.conf
Dir tree built,  /home/eps/ePS/mol

*** Pushing file: mol.batch.orb18_A2U.conf to remote: /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf
File /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf already exists, overwrite? (y/n)  y
bash: /home/eps/github/epsman/shell/ePS_input_write_template_basic_noDefaults.sh: No such file or directory
Uploaded
/home/eps/ePS/epsman-demo/ESclasses/mol.batch.orb18_A2U.conf
 to
/home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf
Set Estart = 1.0
Set Estop = 1.0
Set dE = 1.0
Set EJob = None
Set EJobRange = None
Set precision = 2
E = 1.0:1.0:1.0, 1 points total, 1/1 = 1 job files will be written.
Writing input files on remote...


*** Failed to build job at self.writeInp.
Encountered a bad command exit code!

Command: '~/github/epsman/shell/ePS_input_write_template_basic_noDefaults.sh 1.0 1.0 1 /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf'

Exit code: 127

Stdout: already printed

Stderr: already printed


[8]:
False

Fix missing shell scripts

If the script fails at self.writeInp() this is likely due to the shell scripts for job-writing missing on the host. This currently needs to be fixed manually.

Example failure:

E = 1.0:1.0:1.0, 1 points total, 1/1 = 1 job files will be written.
Writing input files on remote...


*** Failed to build job at self.writeInp.
Encountered a bad command exit code!

Command: '~/github/epsman/shell/ePS_input_write_template_basic_noDefaults.sh 1.0 1.0 1 /home/eps/ePS/test/generators/test.orb18_A2U.conf'

Exit code: 127

Stdout: already printed

Stderr: already printed

TODO: setup VM with scripts

Could push from local repo automatically?

TODO: check/fix permissions on script.

[9]:
# Fix missing shell script

# Set local path to package dir
job.hostDefn['localhost']['scpdir'] = modDir.parent/'shell'

# Ser remote path
# job.hostDefn[job.host]['scpdir'] = Path('~/ePS/shell')
job.hostDefn[job.host]['scpdir'] = Path('/home/eps/ePS/shell')
[10]:
# Available scripts can be listed
job.scrDefn
[10]:
{'basic': 'ePS_input_write_template_basic.sh',
 'basicNoDefaults': 'ePS_input_write_template_basic_noDefaults.sh',
 'basic-heavy': 'ePS_input_write_template_basic-heavy.sh',
 'wf-sph': 'ePS_input_write_template_wf_sph.sh',
 'EDCS': 'ePS_input_write_template_basic-EDCS_noDefaults.sh',
 'nb-tpl-JR-v1': 'ePSproc_epsman_template_dev_051119_JR-single.ipynb',
 'nb-tpl-JR-v2': 'ePSproc_epsman_template_dev_051219_JR-single.ipynb',
 'nb-tpl-JR-v3': 'ePSproc_epsman_template_tidy_100120_JR-single.ipynb',
 'nb-tpl-JR-v4': 'ePSproc_epsman_template_tidy_120120_JR-single.ipynb',
 'nb-tpl-JR-v5': 'ePSproc_epsman_template_tidy_300320_JR-single.ipynb',
 'nb-tpl-JR-v4-EC': 'ePSproc_epsman_template_tidy_300320_JR-E-chunck.ipynb',
 'nb-sh-JR': 'jr_epsProc_nb.sh'}
[11]:
# Set dir on host if missing
# Note this currently uses the low-level Fabric object, should be wrapped!
# UPDATE - should now be cleaner with dir creation on pushFile() below if necessary
# job.c.run('mkdir -p ' + job.hostDefn[job.host]['scpdir'].as_posix())
[12]:
# job.pushFile(job.hostDefn['localhost']['scpdir']/job.scrDefn['basicNoDefaults'], job.hostDefn[job.host]['scpdir'])
job.pushFile(job.hostDefn['localhost']['scpdir']/job.scrDefn['basic'], job.hostDefn[job.host]['scpdir'])

*** Pushing file: /home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic.sh to remote: /home/eps/ePS/shell/ePS_input_write_template_basic.sh
File /home/eps/ePS/shell/ePS_input_write_template_basic.sh already exists, overwrite? (y/n)  y
Uploaded
/home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic.sh
 to
/home/eps/ePS/shell/ePS_input_write_template_basic.sh
[12]:
True
[13]:
job.writeScript
[13]:
'basicNoDefaults'
[14]:
# What about sync for this...?
# This should now work (23/02/22), provided 'scpFile' is set on local machine (it's not set by default).
job.syncFilesDict('scpFile')

*** Syncing files /home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic_noDefaults.sh

        eps-VM:         /home/eps/github/epsman/epsman/shell/ePS_input_write_template_basic_noDefaults.sh       False

*** Can't sync files, self.host or self.hostDefn[host][scpFile] not set.
[14]:
[{'eps-VM': False, 'localhost': None}, None]
[15]:
# Run job writer again...

job.writeInp()
Writing input files on remote...

%%%%%%%%
Job: batch
File: batch.orb18_A2U_E1.0_1_1.0eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U/idy’: File exists
%%%%%%%%
Writing 1 energies to job file:
batch.orb18_A2U_E1.0_1_1.0eV
%%%%%%%%

Results logged to local file: mol.batch.orb18_A2U.conf.log

*** Pushing file: mol.batch.orb18_A2U.conf.log to remote: /home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log
File /home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log already exists, overwrite? (y/n)  y
Uploaded
/home/eps/ePS/epsman-demo/ESclasses/mol.batch.orb18_A2U.conf.log
 to
/home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log

(Above errors should now be fixed…)

Job configuration files

The end-point of the build routine is the creation of a job configuration file & execution of a shell script on the host machine to create ePS input files. This is currently slightly convoluted, but basically aims to:

  • Provide a method for a minimal job definition in the .conf file, which is then used to generate multiple ePS input files for different energies.

  • Make the conf file (somewhat) portable, hence also includes all necessary paths on the host.

  • Ensure minimal file IO between local and host machine.

(In future the shell scripts should be replaced by a python method and better templating.)

[16]:
# The .conf file is also stored locally (defaults to current working dir), and the full string in self.jobSettings
print(f"Configuration (generation) file: {job.genFile}")
print("\n*******")
print(job.jobSettings)
Configuration (generation) file: mol.batch.orb18_A2U.conf

*******


# Set working environment
machine=eps-VM
wrkdir=/home/eps/ePS
scpdir=~/github/epsman/shell

# Settings from ePS_batch_job.sh
ePSpath=/opt/ePolyScat.E3/bin/ePolyScat
jobPath=/home/eps/ePS/jobs
jobComplete=/home/eps/ePS/jobs/completed


# Job definitions, used for dir structure and output naming.
mol=mol
orb=orb18_A2U
job=batch
note='mol, orb 18 (A2U/DAh) ionization, batch batch, None.'


#*******************************************************************************************
# (c) Molecule (job) settings
#

# Global configuration
headerSettings="LMax 30
FegeEng 12.422"

# Job settings
elecStructure=/home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.molden
elecType='molden'
IP=12.422
OrbOccInit='2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 2 4'
OrbOccTarget='2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 1 4'
InitSpinDeg=1
TargSpinDeg=2
SpinDeg=1
InitSym='SG'
TargSym='A2U'
Ssym=(SG SG SG SG SG SG SG SG SG SG SG SG SG SG SG SG A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G A2G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B1G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G B2G PG PG PG PG PG PG PG PG PG PG PG PG PG PG PG PG DG DG DG DG DG DG DG DG DG DG DG DG DG DG DG DG FG FG FG FG FG FG FG FG FG FG FG FG FG FG FG FG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG SU SU SU SU SU SU SU SU SU SU SU SU SU SU SU SU A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U A2U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B1U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U B2U PU PU PU PU PU PU PU PU PU PU PU PU PU PU PU PU DU DU DU DU DU DU DU DU DU DU DU DU DU DU DU DU FU FU FU FU FU FU FU FU FU FU FU FU FU FU FU FU GU GU GU GU GU GU GU GU GU GU GU GU GU GU GU GU)
Csym=(SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU)



Modifying jobs

To update & rewrite the configuration file, make changes to any of the data structures and propagate…

The current build routine implements:

  1. self.esData.setChannel(channel): Set ionization channel for job

  2. self.setJob(): setup basic defaults, including name and paths, for job.

  3. self.esData.setePSinputs(): Set parameter dictionaries self.ePSglobals and self.ePSrecords from inputs, the latter is defined mainly from the electronic structure file (and channel setting).

  4. self.esData.writeInputConf(): Dictionaries > strings for job template

  5. self.setJobInputConfig(): Settings string > template file string

  6. self.writeGenFile(): Template file string > conf file format (adds paths etc.)

  7. self.createJobDirTree(): Creates job tree on host, and pushes generator (conf) file.

  8. self.Elist = em.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = EJob, precision = precision): Set energies for input file generation on host.

  9. self.writeInp(scrType = scrType, wLog = writeInpLog): Write ePS jobs on host, making use of .conf file and shell script as given by scrType.

Any step can be updated/run independently.

TODO: streamline this!

[17]:
# Change something via a method

# For symmetries currently need to run both gen and set functions
# job.esData.genSymList(Ssym = 'SG')  # Set for a single scattering symmetry only - NOTE this sets self.symList, but doesn't propagate
# job.esData.setePSinputs(Ssym = 'SG')  # Set self.ePSglobals and self.ePSrecords from inputs - NOTE this may reset other params too!
                                      # NOTE - need to add some flags here to overwrite!

# TODO: "update" setting for setePSinputs() and/or chain functions more carefully.

# Update 23/02/22: now automatic sym overwrite
job.esData.setePSinputs(Ssym = 'SG')
Updating symList with Ssym=SG, Csym=None
Set self.ePSglobals for global job settings.
{'LMax': 30, 'FegeEng': 12.422}
[18]:
job.esData.symList
[18]:
[('SG', 'SG'),
 ('SG', 'A2G'),
 ('SG', 'B1G'),
 ('SG', 'B2G'),
 ('SG', 'PG'),
 ('SG', 'DG'),
 ('SG', 'FG'),
 ('SG', 'GG'),
 ('SG', 'SU'),
 ('SG', 'A2U'),
 ('SG', 'B1U'),
 ('SG', 'B2U'),
 ('SG', 'PU'),
 ('SG', 'DU'),
 ('SG', 'FU'),
 ('SG', 'GU')]
[19]:
# Change something in job settings manually
job.esData.ePSrecords['IP']=50.0
[20]:
# Propagate - TODO: tidy this up!
job.esData.writeInputConf()  # Dictionaries > strings for job template
job.setJobInputConfig()  # Settings string > template file string

# Print
print(job.jobSettings)


# Job definitions, used for dir structure and output naming.
mol=mol
orb=orb18_A2U
job=batch
note='mol, orb 18 (A2U/DAh) ionization, batch batch, None.'


#*******************************************************************************************
# (c) Molecule (job) settings
#

# Global configuration
headerSettings="LMax 30
FegeEng 12.422"

# Job settings
elecStructure=/home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.molden
elecType='molden'
IP=50.0
OrbOccInit='2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 2 4'
OrbOccTarget='2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 1 4'
InitSpinDeg=1
TargSpinDeg=2
SpinDeg=1
InitSym='SG'
TargSym='A2U'
Ssym=(SG SG SG SG SG SG SG SG SG SG SG SG SG SG SG SG)
Csym=(SG A2G B1G B2G PG DG FG GG SU A2U B1U B2U PU DU FU GU)



[21]:
# Rewrite config to disk (local copy)
job.writeGenFile()

# For remote either push manually, or run job.createJobDirTree()

# TODO: update main build routine with an "update" option!
Written local job conf file (working dir): /home/eps/ePS/epsman-demo/ESclasses/mol.batch.orb18_A2U.conf
[52]:
# Run job writer again to generate .inp files with new settings.

job.writeInp()
Writing input files on remote...

%%%%%%%%
Job: batch
File: batch.orb18_A2U_E10_15_190eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U/idy’: File exists
%%%%%%%%
Writing 13 energies to job file:
batch.orb18_A2U_E10_15_190eV
%%%%%%%%
%%%%%%%%
Job: batch
File: batch.orb18_A2U_E15_15_195eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U/idy’: File exists
%%%%%%%%
Writing 13 energies to job file:
batch.orb18_A2U_E15_15_195eV
%%%%%%%%
%%%%%%%%
Job: batch
File: batch.orb18_A2U_E20_15_200eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U’: File exists
mkdir: cannot create directory ‘/home/eps/ePS/mol/batch/orb18_A2U/idy’: File exists
%%%%%%%%
Writing 13 energies to job file:
batch.orb18_A2U_E20_15_200eV
%%%%%%%%

Results logged to local file: mol.batch.orb18_A2U.conf.log

*** Pushing file: mol.batch.orb18_A2U.conf.log to remote: /home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log
File /home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log already exists, overwrite? (y/n)  y
Uploaded
/home/eps/ePS/epsman-demo/ESclasses/mol.batch.orb18_A2U.conf.log
 to
/home/eps/ePS/mol/batch/orb18_A2U/mol.batch.orb18_A2U.conf.log

Energies & job chuncking

This is currently handled by:

  • self.Elist, 2D numpy array defining energies per file.

  • self.writeInp(), method to write ePS input files per set of energies.

The Elist can be set manually, or via the multiEChunck method. self.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = EJob, EJobRange = None, precision = precision) set energies for input file generation on host.

[22]:
# Currently set list - default is a single point at 1 eV
job.Elist
[22]:
array([[1.]])
[24]:
# Demo some settings
Estart = 10
Estop = 45
dE = 5

job.multiEChunck(Estart = Estart, Estop = Estop, dE = dE)
job.Elist
Set Estart = 10
Set Estop = 45
Set dE = 5
Set precision = 2
E = 10:5:45, 8 points total, 8/8 = 1 job files will be written.
[24]:
array([[10],
       [15],
       [20],
       [25],
       [30],
       [35],
       [40],
       [45]])
[27]:
# Force multiple files if required
job.multiEChunck(Estart = Estart, Estop = Estop, dE = dE, EJob = 5)
job.Elist
Set Estart = 10
Set Estop = 45
Set dE = 5
Set EJob = 5
Set precision = 2
E = 10:5:45, 8 points total, 8/4 = 2 job files will be written.
[27]:
array([[10, 15],
       [20, 25],
       [30, 35],
       [40, 45]])
[28]:
# Large E sets should chunck automatically
# The points per file will default to the greatest common divisor within EJobRange, np.gcd(Elist.size, np.arange(EJobRange[0],EJobRange[1])).max()
# (with some wiggle room).
Estop = 200
job.multiEChunck(Estart = Estart, Estop = Estop, dE = dE)
job.Elist
Set Estart = 10
Set Estop = 200
Set dE = 5
Set precision = 2
E = 10:5:200, 39 points total, 39/13 = 3 job files will be written.
[28]:
array([[ 10,  15,  20],
       [ 25,  30,  35],
       [ 40,  45,  50],
       [ 55,  60,  65],
       [ 70,  75,  80],
       [ 85,  90,  95],
       [100, 105, 110],
       [115, 120, 125],
       [130, 135, 140],
       [145, 150, 155],
       [160, 165, 170],
       [175, 180, 185],
       [190, 195, 200]])

More about ePS input files

In the current implementation, the .conf file is used as the base for writing ePolyScat job files, using one of the shell scripts. This is a bit messy, but allows for easy creation & management of job files on the host only.

Note - this may/should be changed to an all-python templating method in future!

For more general details on ePolyScat input files and options, see the ePolyScat manual; there’s also a brief tutorial in the ePSproc docs (which follows test12.inp from the ePS manual).

Available scripts

A list of current scripts is given in job.scrDefn. Note this currently includes scripts for various purposes.

TODO: change to nested dict by type & including notes!

[34]:
job.scrDefn
[34]:
{'basic': 'ePS_input_write_template_basic.sh',
 'basicNoDefaults': 'ePS_input_write_template_basic_noDefaults.sh',
 'basic-heavy': 'ePS_input_write_template_basic-heavy.sh',
 'wf-sph': 'ePS_input_write_template_wf_sph.sh',
 'EDCS': 'ePS_input_write_template_basic-EDCS_noDefaults.sh',
 'nb-tpl-JR-v1': 'ePSproc_epsman_template_dev_051119_JR-single.ipynb',
 'nb-tpl-JR-v2': 'ePSproc_epsman_template_dev_051219_JR-single.ipynb',
 'nb-tpl-JR-v3': 'ePSproc_epsman_template_tidy_100120_JR-single.ipynb',
 'nb-tpl-JR-v4': 'ePSproc_epsman_template_tidy_120120_JR-single.ipynb',
 'nb-tpl-JR-v5': 'ePSproc_epsman_template_tidy_300320_JR-single.ipynb',
 'nb-tpl-JR-v4-EC': 'ePSproc_epsman_template_tidy_300320_JR-E-chunck.ipynb',
 'nb-sh-JR': 'jr_epsProc_nb.sh'}
[33]:
# Commands executed by self.writeInp() are collected in self.writeLog
job.writeLog
[33]:
[<Result cmd='/home/eps/ePS/shell/ePS_input_write_template_basic_noDefaults.sh 1.0 1.0 1 /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf' exited=0>]
[42]:
# Note these are objects returned by Fabric, so the output can be displayed too
print(job.writeLog[0].stdout)
%%%%%%%%
Job: batch
File: batch.orb18_A2U_E1.0_1_1.0eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
%%%%%%%%
Writing 1 energies to job file:
batch.orb18_A2U_E1.0_1_1.0eV
%%%%%%%%

[35]:
# And outputs in the log file
job.logFile
[35]:
PosixPath('mol.batch.orb18_A2U.conf.log')
[38]:
!more {job.logFile}
ePSman log file, job: mol.batch.orb18_A2U.conf
Running on eps-VM
2022-02-23 13:11

%%%%%%%%
Job: batch
File: batch.orb18_A2U_E1.0_1_1.0eV
Orb: orb18_A2U
Note: mol, orb 18 (A2U/DAh) ionization, batch batch, None.
%%%%%%%%
Base directory: /home/eps/ePS/mol/batch
Job dir: /home/eps/ePS/mol/batch/orb18_A2U
MatE dir: /home/eps/ePS/mol/batch/orb18_A2U/idy
%%%%%%%%
Writing 1 energies to job file:
batch.orb18_A2U_E1.0_1_1.0eV
%%%%%%%%


Files & details

Currently manual - TODO: wrap to methods.

[44]:
# Job path
job.hostDefn[job.host]['jobDir']
[44]:
PosixPath('/home/eps/ePS/mol/batch/orb18_A2U')
[53]:
# Get list of .inp files
fileList = job.getFileList(job.hostDefn[job.host]['jobDir'], fileType='.inp')

***File List (from eps-VM):
/home/eps/ePS/mol/batch/orb18_A2U/batch.orb18_A2U_E1.0_1_1.0eV.inp
/home/eps/ePS/mol/batch/orb18_A2U/batch.orb18_A2U_E10_15_190eV.inp
/home/eps/ePS/mol/batch/orb18_A2U/batch.orb18_A2U_E15_15_195eV.inp
/home/eps/ePS/mol/batch/orb18_A2U/batch.orb18_A2U_E20_15_200eV.inp
[58]:
# To view a sample input file, it can be pulled to the local machine directly,

# TODO: update pullFile with pushFile remote fixes!
# job.pullFile(Path('testInp'), Path(fileList[0]))

# or piped from the host
# job.c.run('more ' + fileList[1])  # Full file, note this might be quite large
nFile = 1
job.c.run('head -60 ' + fileList[nFile])

# TODO: view function for this?

# ePS mol, batch batch, orbital orb18_A2U
# mol, orb 18 (A2U/DAh) ionization, batch batch, None.
# E=10:15:190 (13 points)
#
# File date: Wed 23 Feb 2022 02:58:44 PM EST
# Running on: eps-VM
#
# Configuration: /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf
# Template: Basic photoionization template, 30/09/19

# Master job config settings
LMax 30
FegeEng 12.422

EMax  190    # EMax, maximum asymptotic energy in eV
IPot 12.422
FegeEng 12.422   # Energy correction used in the fege potential


# Set initial and final orbital occupations
OrbOccInit
  2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 2 4
OrbOcc        # occupation of the orbital groups of target
  2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 1 4


# Set electronic structure to read in
Convert '/home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.molden' 'molden2006'
GetBlms
ExpOrb

# Set global symmetries & spins
SpinDeg 1               # Spin degeneracy of the total scattering state (=1 singlet)
TargSym 'A2U'                   # Symmetry of the target state
TargSpinDeg 2    # Target spin degeneracy
InitSym 'SG'                    # Initial state symmetry
InitSpinDeg 1    # Initial state spin degeneracy'

# Set energies
ScatEng 10 25 40 55 70 85 100 115 130 145 160 175 190

#*** Scat - set final state symmetries & do scattering calc. for each set


# Symmetries set 1, SSGCSG
ScatSym 'SG' # Scattering symmetry of total final state
ScatContSym 'SG' # Scattering symmetry of continuum electron
FileName 'MatrixElements' '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSSGCSG.idy' 'REWIND'

GenFormPhIon
DipoleOp
GetPot
PhIon
GetCro


# Symmetries set 2, SSGCA2G
ScatSym 'SG' # Scattering symmetry of total final state
ScatContSym 'A2G' # Scattering symmetry of continuum electron
[58]:
<Result cmd='head -60 /home/eps/ePS/mol/batch/orb18_A2U/batch.orb18_A2U_E10_15_190eV.inp' exited=0>

Job file breakdown

The job inputs basically follow the simple photoionization examples from the ePolyScat manual, so may NOT be suitable in all cases (and may not be the best way to do some things).

The current scripts set commands for ePS to compute photoionization matrix elements for each symmetry and energy, with PhIon, GetCro and DumpIdy commands (see the ePSproc advanced tutorial for a brief intro).

Essentially, there are 3 main segments:

  1. Init job & global settings, including the electronic structure file and list of energies.

[63]:
job.c.run('head -42 ' + fileList[nFile]);

# ePS mol, batch batch, orbital orb18_A2U
# mol, orb 18 (A2U/DAh) ionization, batch batch, None.
# E=10:15:190 (13 points)
#
# File date: Wed 23 Feb 2022 02:58:44 PM EST
# Running on: eps-VM
#
# Configuration: /home/eps/ePS/mol/generators/mol.batch.orb18_A2U.conf
# Template: Basic photoionization template, 30/09/19

# Master job config settings
LMax 30
FegeEng 12.422

EMax  190    # EMax, maximum asymptotic energy in eV
IPot 12.422
FegeEng 12.422   # Energy correction used in the fege potential


# Set initial and final orbital occupations
OrbOccInit
  2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 2 4
OrbOcc        # occupation of the orbital groups of target
  2 2 2 4 2 2 4 4 2 4 2 2 4 2 4 4 2 1 4


# Set electronic structure to read in
Convert '/home/eps/github/epsman/epsman/elecStructure/fileTest/xe_SPKrATZP_rel.molden' 'molden2006'
GetBlms
ExpOrb

# Set global symmetries & spins
SpinDeg 1               # Spin degeneracy of the total scattering state (=1 singlet)
TargSym 'A2U'                   # Symmetry of the target state
TargSpinDeg 2    # Target spin degeneracy
InitSym 'SG'                    # Initial state symmetry
InitSpinDeg 1    # Initial state spin degeneracy'

# Set energies
ScatEng 10 25 40 55 70 85 100 115 130 145 160 175 190

  1. A repeated set of scat runs (via PhIon), for each symmetry pair, and cross-section outputs (via GetCro).

[68]:
job.c.run("sed -n '43,70p' " + fileList[nFile]);
#*** Scat - set final state symmetries & do scattering calc. for each set


# Symmetries set 1, SSGCSG
ScatSym 'SG' # Scattering symmetry of total final state
ScatContSym 'SG' # Scattering symmetry of continuum electron
FileName 'MatrixElements' '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSSGCSG.idy' 'REWIND'

GenFormPhIon
DipoleOp
GetPot
PhIon
GetCro


# Symmetries set 2, SSGCA2G
ScatSym 'SG' # Scattering symmetry of total final state
ScatContSym 'A2G' # Scattering symmetry of continuum electron
FileName 'MatrixElements' '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSSGCA2G.idy' 'REWIND'

GenFormPhIon
DipoleOp
GetPot
PhIon
GetCro


# Symmetries set 3, SSGCB1G
  1. Dumping all matrix elements to file (per energy).

[70]:
job.c.run("tail -n 20 " + fileList[nFile]);
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 10
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 25
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 40
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 55
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 70
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 85
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 100
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 115
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 130
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 145
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 160
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 175
DumpIdy '/home/eps/ePS/mol/batch/orb18_A2U/idy/molSGUCGU.idy' 190





#*** END OF JOB

Versions

[71]:
import scooby
scooby.Report(additional=['epsman', 'fabric', 'cclib'])
[71]:
Wed Feb 23 15:27:58 2022 EST
OS Linux CPU(s) 64 Machine x86_64
Architecture 64bit Environment Jupyter
Python 3.7.10 (default, Feb 26 2021, 18:47:35) [GCC 7.3.0]
epsman 0.0.1 fabric 2.6.0 cclib 1.7
numpy 1.19.2 scipy 1.6.1 IPython 7.21.0
matplotlib 3.3.4 scooby 0.5.6
Intel(R) Math Kernel Library Version 2020.0.2 Product Build 20200624 for Intel(R) 64 architecture applications
[72]:
# Check current Git commit for local ePSproc version
from pathlib import Path
import epsman as em

!git -C {Path(em.__file__).parent} branch
!git -C {Path(em.__file__).parent} log --format="%H" -n 1
  master
* restructure160221
73f10b5bdbf99e97212a639dd390217084ec0c09
[73]:
# Check current remote commits
!git ls-remote --heads git://github.com/phockett/epsman
21b4357a169baf9fa7887c68bd1cf8f92c59642c        refs/heads/master
73f10b5bdbf99e97212a639dd390217084ec0c09        refs/heads/restructure160221
[ ]:

[ ]: