Merge remote-tracking branch 'github/master' into collected-small-changes
# Conflicts: # python/lammps/__init__.py # python/lammps/core.py
This commit is contained in:
@ -377,14 +377,6 @@ make MPI calls directly from Python in your script, if you desire.
|
||||
We have tested this with `MPI for Python <https://mpi4py.readthedocs.io/>`_
|
||||
(aka mpi4py) and you will find installation instruction for it below.
|
||||
|
||||
.. note::
|
||||
|
||||
Older LAMMPS versions were also tested with `PyPar <https://github.com/daleroberts/pypar>`_
|
||||
but we can no longer test it, since it does not work with the Python
|
||||
(3.x) versions on our test servers. Since there have been no updates
|
||||
to PyPar visible in its repository since November 2016 we have to assume
|
||||
it is no longer maintained.
|
||||
|
||||
Installation of mpi4py (version 3.0.3 as of Sep 2020) can be done as
|
||||
follows:
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
{% elif show_source and has_source and sourcename %}
|
||||
<a href="{{ pathto('_sources/' + sourcename, true)|e }}" rel="nofollow"> {{ _('View page source') }}</a>
|
||||
{% endif %}
|
||||
<a href="http://lammps.sandia.gov">Website</a>
|
||||
<a href="https://www.lammps.org">Website</a>
|
||||
<a href="Commands_all.html">Commands</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
@ -38,10 +38,11 @@
|
||||
{% if favicon %}
|
||||
<link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
|
||||
{% endif %}
|
||||
{# CANONICAL URL #}
|
||||
{% if theme_canonical_url %}
|
||||
<link rel="canonical" href="{{ theme_canonical_url }}{{ pagename }}.html"/>
|
||||
{% endif %}
|
||||
|
||||
{#- CANONICAL URL #}
|
||||
{%- if pageurl %}
|
||||
<link rel="canonical" href="{{ pageurl|e }}" />
|
||||
{%- endif -%}
|
||||
|
||||
{# JAVASCRIPTS #}
|
||||
{%- block scripts %}
|
||||
|
||||
@ -4,7 +4,6 @@ stylesheet = css/theme.css
|
||||
pygments_style = default
|
||||
|
||||
[options]
|
||||
canonical_url =
|
||||
analytics_id =
|
||||
collapse_navigation = False
|
||||
sticky_navigation = False
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
from datetime import date
|
||||
|
||||
has_enchant = False
|
||||
try:
|
||||
@ -76,7 +77,7 @@ master_doc = 'Manual'
|
||||
|
||||
# General information about the project.
|
||||
project = 'LAMMPS'
|
||||
copyright = '2003-2020 Sandia Corporation'
|
||||
copyright = '2003-{} Sandia Corporation'.format(date.today().year)
|
||||
|
||||
def get_lammps_version():
|
||||
import os
|
||||
@ -255,6 +256,8 @@ htmlhelp_basename = 'LAMMPSdoc'
|
||||
|
||||
html_permalinks = True
|
||||
|
||||
html_baseurl = os.environ.get('LAMMPS_WEBSITE_BASEURL', '')
|
||||
|
||||
if 'epub' in sys.argv:
|
||||
html_math_renderer = 'imgmath'
|
||||
else:
|
||||
|
||||
@ -2631,7 +2631,6 @@ PyLammps
|
||||
pymbar
|
||||
pymodule
|
||||
pymol
|
||||
pypar
|
||||
pythonic
|
||||
pytorch
|
||||
pyy
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
# The units of Cij are whatever was used in log.lammps (usually GPa)
|
||||
# The units of Sij are the inverse of that (usually 1/GPa)
|
||||
|
||||
from __future__ import print_function
|
||||
from numpy import zeros
|
||||
from numpy.linalg import inv
|
||||
|
||||
@ -44,9 +45,8 @@ cindices[20] = (4,5)
|
||||
|
||||
# open logfile
|
||||
|
||||
logfile = open("log.lammps",'r')
|
||||
|
||||
txt = logfile.read()
|
||||
with open("log.lammps",'r') as logfile:
|
||||
txt = logfile.read()
|
||||
|
||||
# search for 21 elastic constants
|
||||
|
||||
@ -56,7 +56,7 @@ s2 = 0
|
||||
for ival in range(nvals):
|
||||
s1 = txt.find(valstr,s2)
|
||||
if (s1 == -1):
|
||||
print "Failed to find elastic constants in log file"
|
||||
print("Failed to find elastic constants in log file")
|
||||
exit(1)
|
||||
s1 += 1
|
||||
s2 = txt.find("\n",s1)
|
||||
@ -67,11 +67,11 @@ for ival in range(nvals):
|
||||
c[i1,i2] = float(words[valpos])
|
||||
c[i2,i1] = c[i1,i2]
|
||||
|
||||
print "C tensor [GPa]"
|
||||
print("C tensor [GPa]")
|
||||
for i in range(6):
|
||||
for j in range(6):
|
||||
print "%10.8g " % c[i][j],
|
||||
print
|
||||
print("%10.8g " % c[i][j], end="")
|
||||
print()
|
||||
|
||||
# apply factor of 2 to columns of off-diagonal elements
|
||||
|
||||
@ -87,9 +87,8 @@ for i in range(6):
|
||||
for j in range(3,6):
|
||||
s[i][j] *= 0.5
|
||||
|
||||
print "S tensor [1/GPa]"
|
||||
print("S tensor [1/GPa]")
|
||||
for i in range(6):
|
||||
for j in range(6):
|
||||
print "%10.8g " % s[i][j],
|
||||
print
|
||||
|
||||
print("%10.8g " % s[i][j], end="")
|
||||
print()
|
||||
|
||||
1
python/.gitignore
vendored
1
python/.gitignore
vendored
@ -1 +1,2 @@
|
||||
/build
|
||||
/*.egg-info
|
||||
|
||||
@ -44,6 +44,7 @@ split.py parallel example
|
||||
mc.py Monte Carlo energy relaxation wrapper on LAMMPS
|
||||
gui.py GUI go/stop/temperature-slider to control LAMMPS
|
||||
plot.py real-time temperature plot with GnuPlot via Pizza.py
|
||||
matplotlib_plot.py real-time temperature plot with Matplotlib via Pizza.py
|
||||
viz_tool.py real-time viz via some viz package
|
||||
vizplotgui_tool.py combination of viz.py and plot.py and gui.py
|
||||
|
||||
@ -100,24 +101,24 @@ split.py in.simple # can run in parallel (see below)
|
||||
|
||||
gui.py in.gui 100
|
||||
plot.py in.plot 10 1000 thermo_temp
|
||||
matplotlib_plot.py in.plot 10 1000 thermo_temp
|
||||
viz_tool.py in.viz 100 5000
|
||||
vizplotgui_tool.py in.viz 100 thermo_temp
|
||||
|
||||
To run LAMMPS in parallel from Python, so something like this:
|
||||
|
||||
% mpirun -np 4 simple.py in.simple
|
||||
% mpirun -np 4 python split.py in.simple
|
||||
% mpirun -np P simple.py in.simple
|
||||
% mpirun -np P python split.py in.simple
|
||||
|
||||
If you run simple.py as-is, this will invoke P instances of a
|
||||
one-processor run, where both Python and LAMMPS will run on single
|
||||
processors. Each running job will read the same input file, and write
|
||||
to same log.lammps file, which isn't too useful.
|
||||
|
||||
However, if you have either the Pypar or mpi4py packages installed in
|
||||
your Python, and uncomment the Pypar or mpi4py code in simple.py, then
|
||||
the above commands will invoke 1 instance of a P-processor run. Both
|
||||
Python and LAMMPS will run on P processors. The job will read the
|
||||
input file and write a single log.lammps file.
|
||||
However, if you have the mpi4py Python package installed and uncomment mpi4py
|
||||
code in simple.py, then the above commands will invoke 1 instance of a
|
||||
P-processor run. Both Python and LAMMPS will run on P processors. The job will
|
||||
read the input file and write a single log.lammps file.
|
||||
|
||||
The split.py script can also be run in parallel. It uses mpi4py
|
||||
version 2.0.0 (or later), which makes it possible to pass a
|
||||
|
||||
1
python/examples/.gitignore
vendored
Normal file
1
python/examples/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/tmp*
|
||||
@ -7,8 +7,7 @@
|
||||
# in.lammps = LAMMPS input script
|
||||
# Nfreq = query GUI every this many steps
|
||||
|
||||
# IMPORTANT: this script cannot yet be run in parallel via Pypar,
|
||||
# because I can't seem to do a MPI-style broadcast in Pypar
|
||||
# IMPORTANT: this script cannot yet be run in parallel
|
||||
|
||||
from __future__ import print_function
|
||||
import sys,time
|
||||
@ -39,10 +38,6 @@ infile = sys.argv[1]
|
||||
nfreq = int(sys.argv[2])
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -110,7 +105,3 @@ while 1:
|
||||
if runflag: running = 1
|
||||
else: running = 0
|
||||
time.sleep(0.01)
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -12,9 +12,6 @@
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
sys.path.append("./pizza")
|
||||
import matplotlib
|
||||
matplotlib.use('tkagg')
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# parse command line
|
||||
@ -30,10 +27,10 @@ nsteps = int(sys.argv[3])
|
||||
compute = sys.argv[4]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -79,15 +76,16 @@ while ntimestep < nsteps:
|
||||
ax = plt.gca()
|
||||
ax.relim()
|
||||
ax.autoscale_view(True, True, True)
|
||||
fig.canvas.draw()
|
||||
plt.pause(0.001)
|
||||
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
if me == 0:
|
||||
if sys.version_info[0] == 3:
|
||||
input("Press Enter to exit...")
|
||||
else:
|
||||
else:
|
||||
raw_input("Press Enter to exit...")
|
||||
|
||||
@ -275,7 +275,7 @@ class dump:
|
||||
print("no column assignments made")
|
||||
elif len(self.names):
|
||||
print("assigned columns:",self.names2str())
|
||||
elif self.snaps[0].atoms == None:
|
||||
elif self.snaps[0].atoms is None:
|
||||
print("no column assignments made")
|
||||
elif len(self.snaps[0].atoms[0]) == 5:
|
||||
self.map(1,"id",2,"type",3,"x",4,"y",5,"z")
|
||||
@ -969,12 +969,12 @@ class dump:
|
||||
xhi = yhi = zhi = None
|
||||
for snap in self.snaps:
|
||||
if not snap.tselect: continue
|
||||
if xlo == None or snap.xlo < xlo: xlo = snap.xlo
|
||||
if xhi == None or snap.xhi > xhi: xhi = snap.xhi
|
||||
if ylo == None or snap.ylo < ylo: ylo = snap.ylo
|
||||
if yhi == None or snap.yhi > yhi: yhi = snap.yhi
|
||||
if zlo == None or snap.zlo < zlo: zlo = snap.zlo
|
||||
if zhi == None or snap.zhi > zhi: zhi = snap.zhi
|
||||
if xlo is None or snap.xlo < xlo: xlo = snap.xlo
|
||||
if xhi is None or snap.xhi > xhi: xhi = snap.xhi
|
||||
if ylo is None or snap.ylo < ylo: ylo = snap.ylo
|
||||
if yhi is None or snap.yhi > yhi: yhi = snap.yhi
|
||||
if zlo is None or snap.zlo < zlo: zlo = snap.zlo
|
||||
if zhi is None or snap.zhi > zhi: zhi = snap.zhi
|
||||
return [xlo,ylo,zlo,xhi,yhi,zhi]
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@ -851,7 +851,7 @@ class gl:
|
||||
ncolor = self.vizinfo.ntcolor
|
||||
for tri in self.tridraw:
|
||||
itype = int(tri[1])
|
||||
if itype > ncolor: raise StandardError("tri type too big")
|
||||
if itype > ncolor: raise Exception("tri type too big")
|
||||
red,green,blue = self.vizinfo.tcolor[itype]
|
||||
glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,[red,green,blue,1.0]);
|
||||
glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,self.shiny);
|
||||
@ -909,7 +909,7 @@ class gl:
|
||||
ymin >= ylo and ymax <= yhi and zmin >= zlo and zmax <= zhi:
|
||||
if bond[10] > bound: continue
|
||||
itype = int(bond[1])
|
||||
if itype > ncolor: raise StandardError("bond type too big")
|
||||
if itype > ncolor: raise Exception("bond type too big")
|
||||
red,green,blue = self.vizinfo.bcolor[itype]
|
||||
rad = self.vizinfo.brad[itype]
|
||||
glPushMatrix()
|
||||
@ -941,7 +941,7 @@ class gl:
|
||||
ymin >= ylo and ymax <= yhi and \
|
||||
zmin >= zlo and zmax <= zhi:
|
||||
itype = int(tri[1])
|
||||
if itype > ncolor: raise StandardError("tri type too big")
|
||||
if itype > ncolor: raise Exception("tri type too big")
|
||||
red,green,blue = self.vizinfo.tcolor[itype]
|
||||
glMaterialfv(GL_FRONT_AND_BACK,GL_EMISSION,
|
||||
[red,green,blue,1.0]);
|
||||
|
||||
@ -87,12 +87,13 @@ g.curve(N,'r') set color of curve N
|
||||
|
||||
# Imports and external programs
|
||||
|
||||
import types, os
|
||||
import os
|
||||
import sys
|
||||
|
||||
try: from DEFAULTS import PIZZA_GNUPLOT
|
||||
except: PIZZA_GNUPLOT = "gnuplot -p"
|
||||
except ImportError: PIZZA_GNUPLOT = "gnuplot -p"
|
||||
try: from DEFAULTS import PIZZA_GNUTERM
|
||||
except: PIZZA_GNUTERM = "x11"
|
||||
except ImportError: PIZZA_GNUTERM = "x11"
|
||||
|
||||
# Class definition
|
||||
|
||||
@ -122,6 +123,9 @@ class gnu:
|
||||
|
||||
def enter(self):
|
||||
while 1:
|
||||
if sys.version_info[0] == 3:
|
||||
command = input("gnuplot> ")
|
||||
else:
|
||||
command = raw_input("gnuplot> ")
|
||||
if command == "quit" or command == "exit": return
|
||||
self.__call__(command)
|
||||
@ -136,7 +140,7 @@ class gnu:
|
||||
self.export(file,linear,vectors[0])
|
||||
self.figures[self.current-1].ncurves = 1
|
||||
else:
|
||||
if len(vectors) % 2: raise StandardError("vectors must come in pairs")
|
||||
if len(vectors) % 2: raise Exception("vectors must come in pairs")
|
||||
for i in range(0,len(vectors),2):
|
||||
file = self.file + ".%d.%d" % (self.current,i/2+1)
|
||||
self.export(file,vectors[i],vectors[i+1])
|
||||
@ -170,7 +174,7 @@ class gnu:
|
||||
def export(self,filename,*vectors):
|
||||
n = len(vectors[0])
|
||||
for vector in vectors:
|
||||
if len(vector) != n: raise StandardError("vectors must be same length")
|
||||
if len(vector) != n: raise Exception("vectors must be same length")
|
||||
f = open(filename,'w')
|
||||
nvec = len(vectors)
|
||||
for i in range(n):
|
||||
|
||||
@ -68,7 +68,7 @@ index,time,flag = p.iterator(1)
|
||||
|
||||
# Imports and external programs
|
||||
|
||||
import sys, types, glob, urllib
|
||||
import sys, glob, urllib
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
@ -93,7 +93,7 @@ class pdbfile:
|
||||
elif len(args) == 2:
|
||||
filestr = args[0]
|
||||
self.data = args[1]
|
||||
else: raise StandardError("invalid args for pdb()")
|
||||
else: raise Exception("invalid args for pdb()")
|
||||
|
||||
# flist = full list of all PDB input file names
|
||||
# append .pdb if needed
|
||||
@ -104,17 +104,17 @@ class pdbfile:
|
||||
for file in list:
|
||||
if '*' in file: flist += glob.glob(file)
|
||||
else: flist.append(file)
|
||||
for i in xrange(len(flist)):
|
||||
for i in range(len(flist)):
|
||||
if flist[i][-4:] != ".pdb": flist[i] += ".pdb"
|
||||
if len(flist) == 0:
|
||||
raise StandardError("no PDB file specified")
|
||||
raise Exception("no PDB file specified")
|
||||
self.files = flist
|
||||
else: self.files = []
|
||||
|
||||
if len(self.files) > 1 and self.data:
|
||||
raise StandardError("cannot use multiple PDB files with data object")
|
||||
raise Exception("cannot use multiple PDB files with data object")
|
||||
if len(self.files) == 0 and not self.data:
|
||||
raise StandardError("no input PDB file(s)")
|
||||
raise Exception("no input PDB file(s)")
|
||||
|
||||
# grab PDB file from http://rcsb.org if not a local file
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ class vizinfo:
|
||||
# if list of types has a 0, increment each type value
|
||||
|
||||
if 0 in ids:
|
||||
for i in xrange(len(ids)): ids[i] += 1
|
||||
for i in range(len(ids)): ids[i] += 1
|
||||
|
||||
# extend storage list if necessary
|
||||
# extend other arrays for same "which" so that gl::make_atom_calllist
|
||||
@ -109,7 +109,7 @@ class vizinfo:
|
||||
ntypes = len(ids)
|
||||
nrgbs = len(rgbs)
|
||||
|
||||
for i in xrange(ntypes):
|
||||
for i in range(ntypes):
|
||||
id = ids[i]
|
||||
|
||||
if rgbs[0] == "loop":
|
||||
@ -157,7 +157,7 @@ class vizinfo:
|
||||
# if list of types has a 0, increment each type value
|
||||
|
||||
if 0 in ids:
|
||||
for i in xrange(len(ids)): ids[i] += 1
|
||||
for i in range(len(ids)): ids[i] += 1
|
||||
|
||||
# extend storage list if necessary
|
||||
# extend other arrays for same "which" so that gl::make_atom_calllist
|
||||
@ -220,7 +220,7 @@ class vizinfo:
|
||||
# if list of types has a 0, increment each type value
|
||||
|
||||
if 0 in ids:
|
||||
for i in xrange(len(ids)): ids[i] += 1
|
||||
for i in range(len(ids)): ids[i] += 1
|
||||
|
||||
# extend storage list if necessary
|
||||
# extend other arrays for same "which" so that gl::make_atom_calllist
|
||||
@ -234,7 +234,7 @@ class vizinfo:
|
||||
# if list lengths match, set directly, else set types to 1st fill value
|
||||
|
||||
if len(fills) == len(ids):
|
||||
for i in xrange(len(ids)): self.tfill[ids[i]] = int(fills[i])
|
||||
for i in range(len(ids)): self.tfill[ids[i]] = int(fills[i])
|
||||
else:
|
||||
for id in ids: self.tfill[id] = int(fills[0])
|
||||
|
||||
|
||||
@ -45,17 +45,16 @@ v.debug([True|False]) display generated VMD script commands?
|
||||
|
||||
# Imports and external programs
|
||||
|
||||
import types, os
|
||||
import numpy
|
||||
import os
|
||||
|
||||
try: from DEFAULTS import PIZZA_VMDNAME
|
||||
except: PIZZA_VMDNAME = "vmd"
|
||||
except ImportError: PIZZA_VMDNAME = "vmd"
|
||||
try: from DEFAULTS import PIZZA_VMDDIR
|
||||
except: PIZZA_VMDDIR = "/usr/local/lib/vmd"
|
||||
except ImportError: PIZZA_VMDDIR = "/usr/local/lib/vmd"
|
||||
try: from DEFAULTS import PIZZA_VMDDEV
|
||||
except: PIZZA_VMDDEV = "win"
|
||||
except ImportError: PIZZA_VMDDEV = "win"
|
||||
try: from DEFAULTS import PIZZA_VMDARCH
|
||||
except: PIZZA_VMDARCH = "LINUXAMD64"
|
||||
except ImportError: PIZZA_VMDARCH = "LINUXAMD64"
|
||||
|
||||
# try these settings for a Mac
|
||||
#PIZZA_VMDNAME = "vmd"
|
||||
@ -64,7 +63,7 @@ except: PIZZA_VMDARCH = "LINUXAMD64"
|
||||
#PIZZA_VMDARCH = "MACOSXX86"
|
||||
|
||||
try: import pexpect
|
||||
except:
|
||||
except ImportError:
|
||||
print("pexpect from http://pypi.python.org/pypi/pexpect", \
|
||||
"is required for vmd tool")
|
||||
raise
|
||||
|
||||
@ -28,10 +28,10 @@ nsteps = int(sys.argv[3])
|
||||
compute = sys.argv[4]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -57,7 +57,7 @@ if me == 0:
|
||||
gn = gnu()
|
||||
gn.plot(xaxis,yaxis)
|
||||
gn.xrange(0,nsteps)
|
||||
gn.title(compute,"Timestep","Temperature")
|
||||
gn.title(compute.replace('_', ' '),"Timestep","Temperature")
|
||||
|
||||
# run nfreq steps at a time w/out pre/post, query compute, refresh plot
|
||||
|
||||
@ -71,6 +71,5 @@ while ntimestep < nsteps:
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
# Parallel syntax: mpirun -np 4 simple.py in.lammps
|
||||
# in.lammps = LAMMPS input script
|
||||
# also need to uncomment either Pypar or mpi4py sections below
|
||||
# also need to uncomment mpi4py sections below
|
||||
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
@ -27,11 +27,6 @@ infile = sys.argv[1]
|
||||
|
||||
me = 0
|
||||
|
||||
# uncomment this if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
@ -133,8 +128,5 @@ lmp.reset_box([0,0,0],[10,10,8],0,0,0)
|
||||
boxlo,boxhi,xy,yz,xz,periodicity,box_change = lmp.extract_box()
|
||||
if me == 0: print("Box info",boxlo,boxhi,xy,yz,xz,periodicity,box_change)
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
|
||||
@ -28,10 +28,10 @@ nfreq = int(sys.argv[2])
|
||||
nsteps = int(sys.argv[3])
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -68,6 +68,5 @@ while ntimestep < nsteps:
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -24,10 +24,10 @@ nfreq = int(sys.argv[2])
|
||||
nsteps = int(sys.argv[3])
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -83,6 +83,5 @@ while ntimestep < nsteps:
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -24,10 +24,10 @@ nfreq = int(sys.argv[2])
|
||||
nsteps = int(sys.argv[3])
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -78,6 +78,5 @@ while ntimestep < nsteps:
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -24,10 +24,10 @@ nfreq = int(sys.argv[2])
|
||||
nsteps = int(sys.argv[3])
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
# uncomment this if running in parallel via mpi4py
|
||||
#from mpi4py import MPI
|
||||
#me = MPI.COMM_WORLD.Get_rank()
|
||||
#nprocs = MPI.COMM_WORLD.Get_size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -87,6 +87,5 @@ if me == 0:
|
||||
#v.enter()
|
||||
#v.stop()
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
# uncomment if running in parallel via mpi4py
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
# compute-ID = ID of compute that calculates temperature
|
||||
# (or any other scalar quantity)
|
||||
|
||||
# IMPORTANT: this script cannot yet be run in parallel via Pypar,
|
||||
# because I can't seem to do a MPI-style broadcast in Pypar
|
||||
|
||||
from __future__ import print_function
|
||||
import sys,os,time
|
||||
sys.path.append("./pizza")
|
||||
@ -59,10 +56,6 @@ nfreq = int(sys.argv[2])
|
||||
compute = sys.argv[3]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -163,7 +156,3 @@ while 1:
|
||||
time.sleep(0.01)
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
# compute-ID = ID of compute that calculates temperature
|
||||
# (or any other scalar quantity)
|
||||
|
||||
# IMPORTANT: this script cannot yet be run in parallel via Pypar,
|
||||
# because I can't seem to do a MPI-style broadcast in Pypar
|
||||
|
||||
from __future__ import print_function
|
||||
import sys,time
|
||||
sys.path.append("./pizza")
|
||||
@ -55,10 +52,6 @@ nfreq = int(sys.argv[2])
|
||||
compute = sys.argv[3]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -176,7 +169,3 @@ while 1:
|
||||
time.sleep(0.01)
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
# compute-ID = ID of compute that calculates temperature
|
||||
# (or any other scalar quantity)
|
||||
|
||||
# IMPORTANT: this script cannot yet be run in parallel via Pypar,
|
||||
# because I can't seem to do a MPI-style broadcast in Pypar
|
||||
|
||||
from __future__ import print_function
|
||||
import sys,time
|
||||
sys.path.append("./pizza")
|
||||
@ -57,10 +54,6 @@ nfreq = int(sys.argv[2])
|
||||
compute = sys.argv[3]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -172,7 +165,3 @@ while 1:
|
||||
time.sleep(0.01)
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -9,9 +9,6 @@
|
||||
# compute-ID = ID of compute that calculates temperature
|
||||
# (or any other scalar quantity)
|
||||
|
||||
# IMPORTANT: this script cannot yet be run in parallel via Pypar,
|
||||
# because I can't seem to do a MPI-style broadcast in Pypar
|
||||
|
||||
from __future__ import print_function
|
||||
import sys,time
|
||||
sys.path.append("./pizza")
|
||||
@ -56,10 +53,6 @@ nfreq = int(sys.argv[2])
|
||||
compute = sys.argv[3]
|
||||
|
||||
me = 0
|
||||
# uncomment if running in parallel via Pypar
|
||||
#import pypar
|
||||
#me = pypar.rank()
|
||||
#nprocs = pypar.size()
|
||||
|
||||
from lammps import lammps
|
||||
lmp = lammps()
|
||||
@ -171,7 +164,3 @@ while 1:
|
||||
time.sleep(0.01)
|
||||
|
||||
lmp.command("run 0 pre no post yes")
|
||||
|
||||
# uncomment if running in parallel via Pypar
|
||||
#print("Proc %d out of %d procs has" % (me,nprocs), lmp)
|
||||
#pypar.finalize()
|
||||
|
||||
@ -25,18 +25,18 @@ def get_version_number():
|
||||
|
||||
vstring = None
|
||||
if version_info.major == 3 and version_info.minor >= 8:
|
||||
from importlib.metadata import version
|
||||
from importlib.metadata import version, PackageNotFoundError
|
||||
try:
|
||||
vstring = version('lammps')
|
||||
except: # lgtm [py/catch-base-exception]
|
||||
except PackageNotFoundError:
|
||||
# nothing to do, ignore
|
||||
pass
|
||||
|
||||
else:
|
||||
from pkg_resources import get_distribution
|
||||
from pkg_resources import get_distribution, DistributionNotFound
|
||||
try:
|
||||
vstring = get_distribution('lammps').version
|
||||
except: # lgtm [py/catch-base-exception]
|
||||
except DistributionNotFound:
|
||||
# nothing to do, ignore
|
||||
pass
|
||||
|
||||
|
||||
@ -11,8 +11,6 @@
|
||||
# See the README file in the top-level LAMMPS directory.
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
from ctypes import c_int, c_int32, c_int64
|
||||
|
||||
# various symbolic constants to be used
|
||||
# in certain calls to select data formats
|
||||
LAMMPS_AUTODETECT = None
|
||||
@ -42,6 +40,7 @@ LMP_VAR_ATOM = 1
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
def get_ctypes_int(size):
|
||||
from ctypes import c_int, c_int32, c_int64
|
||||
if size == 4:
|
||||
return c_int32
|
||||
elif size == 8:
|
||||
|
||||
@ -100,7 +100,7 @@ class lammps(object):
|
||||
|
||||
try:
|
||||
if ptr: self.lib = CDLL("",RTLD_GLOBAL)
|
||||
except: # lgtm [py/catch-base-exception]
|
||||
except OSError:
|
||||
self.lib = None
|
||||
|
||||
# load liblammps.so unless name is given
|
||||
@ -307,13 +307,12 @@ class lammps(object):
|
||||
from mpi4py import __version__ as mpi4py_version
|
||||
# tested to work with mpi4py versions 2 and 3
|
||||
self.has_mpi4py = mpi4py_version.split('.')[0] in ['2','3']
|
||||
except: # lgtm [py/catch-base-exception]
|
||||
except ImportError:
|
||||
# ignore failing import
|
||||
pass
|
||||
|
||||
# if no ptr provided, create an instance of LAMMPS
|
||||
# don't know how to pass an MPI communicator from PyPar
|
||||
# but we can pass an MPI communicator from mpi4py v2.0.0 and later
|
||||
# we can pass an MPI communicator from mpi4py v2.0.0 and later
|
||||
# no_mpi call lets LAMMPS use MPI_COMM_WORLD
|
||||
# cargs = array of C strings from args
|
||||
# if ptr, then are embedding Python in LAMMPS input script
|
||||
@ -1134,10 +1133,10 @@ class lammps(object):
|
||||
with ExceptionCheck(self):
|
||||
if dtype == 0:
|
||||
data = ((count*natoms)*c_int)()
|
||||
self.lib.lammps_gather_atoms(self.lmp,name,type,count,data)
|
||||
self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data)
|
||||
elif dtype == 1:
|
||||
data = ((count*natoms)*c_double)()
|
||||
self.lib.lammps_gather_atoms(self.lmp,name,type,count,data)
|
||||
self.lib.lammps_gather_atoms(self.lmp,name,dtype,count,data)
|
||||
else:
|
||||
return None
|
||||
return data
|
||||
@ -1150,10 +1149,10 @@ class lammps(object):
|
||||
with ExceptionCheck(self):
|
||||
if dtype == 0:
|
||||
data = ((count*natoms)*c_int)()
|
||||
self.lib.lammps_gather_atoms_concat(self.lmp,name,type,count,data)
|
||||
self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data)
|
||||
elif dtype == 1:
|
||||
data = ((count*natoms)*c_double)()
|
||||
self.lib.lammps_gather_atoms_concat(self.lmp,name,type,count,data)
|
||||
self.lib.lammps_gather_atoms_concat(self.lmp,name,dtype,count,data)
|
||||
else:
|
||||
return None
|
||||
return data
|
||||
|
||||
@ -8,8 +8,14 @@ LAMMPS_PYTHON_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
LAMMPS_DIR = os.path.dirname(LAMMPS_PYTHON_DIR)
|
||||
LAMMPS_SOURCE_DIR = os.path.join(LAMMPS_DIR, 'src')
|
||||
|
||||
if not os.path.exists(LAMMPS_SOURCE_DIR):
|
||||
# allows installing and building wheel from current directory
|
||||
LAMMPS_DIR = os.path.realpath(os.path.join(os.environ['PWD'], '..'))
|
||||
LAMMPS_SOURCE_DIR = os.path.join(LAMMPS_DIR, 'src')
|
||||
|
||||
def get_lammps_version():
|
||||
with open(os.path.join(LAMMPS_SOURCE_DIR, 'version.h'), 'r') as f:
|
||||
version_h_file = os.path.join(LAMMPS_SOURCE_DIR, 'version.h')
|
||||
with open(version_h_file, 'r') as f:
|
||||
line = f.readline()
|
||||
start_pos = line.find('"')+1
|
||||
end_pos = line.find('"', start_pos)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,9 @@
|
||||
# certain rights in this software. This software is distributed under
|
||||
# the GNU General Public License.
|
||||
|
||||
# for python3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
# gnu tool
|
||||
|
||||
oneline = "Create plots via GnuPlot plotting program"
|
||||
@ -84,12 +87,13 @@ g.curve(N,'r') set color of curve N
|
||||
|
||||
# Imports and external programs
|
||||
|
||||
import types, os
|
||||
import os
|
||||
import sys
|
||||
|
||||
try: from DEFAULTS import PIZZA_GNUPLOT
|
||||
except: PIZZA_GNUPLOT = "gnuplot"
|
||||
except ImportError: PIZZA_GNUPLOT = "gnuplot -p"
|
||||
try: from DEFAULTS import PIZZA_GNUTERM
|
||||
except: PIZZA_GNUTERM = "x11"
|
||||
except ImportError: PIZZA_GNUTERM = "x11"
|
||||
|
||||
# Class definition
|
||||
|
||||
@ -119,6 +123,9 @@ class gnu:
|
||||
|
||||
def enter(self):
|
||||
while 1:
|
||||
if sys.version_info[0] == 3:
|
||||
command = input("gnuplot> ")
|
||||
else:
|
||||
command = raw_input("gnuplot> ")
|
||||
if command == "quit" or command == "exit": return
|
||||
self.__call__(command)
|
||||
@ -133,7 +140,7 @@ class gnu:
|
||||
self.export(file,linear,vectors[0])
|
||||
self.figures[self.current-1].ncurves = 1
|
||||
else:
|
||||
if len(vectors) % 2: raise StandardError,"vectors must come in pairs"
|
||||
if len(vectors) % 2: raise Exception("vectors must come in pairs")
|
||||
for i in range(0,len(vectors),2):
|
||||
file = self.file + ".%d.%d" % (self.current,i/2+1)
|
||||
self.export(file,vectors[i],vectors[i+1])
|
||||
@ -167,13 +174,13 @@ class gnu:
|
||||
def export(self,filename,*vectors):
|
||||
n = len(vectors[0])
|
||||
for vector in vectors:
|
||||
if len(vector) != n: raise StandardError,"vectors must be same length"
|
||||
if len(vector) != n: raise Exception("vectors must be same length")
|
||||
f = open(filename,'w')
|
||||
nvec = len(vectors)
|
||||
for i in xrange(n):
|
||||
for j in xrange(nvec):
|
||||
print >>f,vectors[j][i],
|
||||
print >>f
|
||||
for i in range(n):
|
||||
for j in range(nvec):
|
||||
print(str(vectors[j][i])+" ",file=f,end='')
|
||||
print ("",file=f)
|
||||
f.close()
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
@ -350,7 +357,7 @@ class gnu:
|
||||
|
||||
self.__call__("set key off")
|
||||
cmd = 'plot '
|
||||
for i in range(fig.ncurves):
|
||||
for i in range(int(fig.ncurves)):
|
||||
file = self.file + ".%d.%d" % (self.current,i+1)
|
||||
if len(fig.colors) > i and fig.colors[i]:
|
||||
cmd += "'" + file + "' using 1:2 with line %d, " % fig.colors[i]
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
# certain rights in this software. This software is distributed under
|
||||
# the GNU General Public License.
|
||||
|
||||
# for python3 compatibility
|
||||
from __future__ import print_function
|
||||
|
||||
# pdb tool
|
||||
|
||||
oneline = "Read, write PDB files in combo with LAMMPS snapshots"
|
||||
@ -51,6 +54,7 @@ index,time,flag = p.iterator(1)
|
||||
|
||||
# History
|
||||
# 8/05, Steve Plimpton (SNL): original version
|
||||
# 3/17, Richard Berger (Temple U): improve Python 3 compatibility
|
||||
|
||||
# ToDo list
|
||||
# for generic PDB file (no template) from a LJ unit system,
|
||||
@ -64,7 +68,13 @@ index,time,flag = p.iterator(1)
|
||||
|
||||
# Imports and external programs
|
||||
|
||||
import sys, types, glob, urllib
|
||||
import sys, glob, urllib
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
string_types = str,
|
||||
else:
|
||||
string_types = basestring
|
||||
|
||||
# Class definition
|
||||
|
||||
@ -74,7 +84,7 @@ class pdbfile:
|
||||
|
||||
def __init__(self,*args):
|
||||
if len(args) == 1:
|
||||
if type(args[0]) is types.StringType:
|
||||
if type(args[0]) is string_types:
|
||||
filestr = args[0]
|
||||
self.data = None
|
||||
else:
|
||||
@ -83,7 +93,7 @@ class pdbfile:
|
||||
elif len(args) == 2:
|
||||
filestr = args[0]
|
||||
self.data = args[1]
|
||||
else: raise StandardError, "invalid args for pdb()"
|
||||
else: raise Exception("invalid args for pdb()")
|
||||
|
||||
# flist = full list of all PDB input file names
|
||||
# append .pdb if needed
|
||||
@ -94,17 +104,17 @@ class pdbfile:
|
||||
for file in list:
|
||||
if '*' in file: flist += glob.glob(file)
|
||||
else: flist.append(file)
|
||||
for i in xrange(len(flist)):
|
||||
for i in range(len(flist)):
|
||||
if flist[i][-4:] != ".pdb": flist[i] += ".pdb"
|
||||
if len(flist) == 0:
|
||||
raise StandardError,"no PDB file specified"
|
||||
raise Exception("no PDB file specified")
|
||||
self.files = flist
|
||||
else: self.files = []
|
||||
|
||||
if len(self.files) > 1 and self.data:
|
||||
raise StandardError, "cannot use multiple PDB files with data object"
|
||||
raise Exception("cannot use multiple PDB files with data object")
|
||||
if len(self.files) == 0 and not self.data:
|
||||
raise StandardError, "no input PDB file(s)"
|
||||
raise Exception("no input PDB file(s)")
|
||||
|
||||
# grab PDB file from http://rcsb.org if not a local file
|
||||
|
||||
@ -112,7 +122,7 @@ class pdbfile:
|
||||
try:
|
||||
open(self.files[0],'r').close()
|
||||
except:
|
||||
print "downloading %s from http://rcsb.org" % self.files[0]
|
||||
print("downloading %s from http://rcsb.org" % self.files[0])
|
||||
fetchstr = "http://www.rcsb.org/pdb/cgi/export.cgi/%s?format=PDB&pdbId=2cpk&compression=None" % self.files[0]
|
||||
urllib.urlretrieve(fetchstr,self.files[0])
|
||||
|
||||
@ -142,20 +152,20 @@ class pdbfile:
|
||||
which,time,flag = self.data.iterator(flag)
|
||||
if flag == -1: break
|
||||
self.convert(f,which)
|
||||
print >>f,"END"
|
||||
print time,
|
||||
print("END",file=f)
|
||||
print(time,end='')
|
||||
sys.stdout.flush()
|
||||
n += 1
|
||||
|
||||
else:
|
||||
for file in self.files:
|
||||
f.write(open(file,'r').read())
|
||||
print >>f,"END"
|
||||
print file,
|
||||
print("END",file=f)
|
||||
print(file,end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
f.close()
|
||||
print "\nwrote %d datasets to %s in PDB format" % (n,file)
|
||||
print("\nwrote %d datasets to %s in PDB format" % (n,file))
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# write series of numbered PDB files
|
||||
@ -190,7 +200,7 @@ class pdbfile:
|
||||
self.convert(f,which)
|
||||
f.close()
|
||||
|
||||
print time,
|
||||
print(time,end='')
|
||||
sys.stdout.flush()
|
||||
n += 1
|
||||
|
||||
@ -210,12 +220,12 @@ class pdbfile:
|
||||
f = open(file,'w')
|
||||
f.write(open(infile,'r').read())
|
||||
f.close()
|
||||
print file,
|
||||
print(file,end='')
|
||||
sys.stdout.flush()
|
||||
|
||||
n += 1
|
||||
|
||||
print "\nwrote %d datasets to %s*.pdb in PDB format" % (n,root)
|
||||
print("\nwrote %d datasets to %s*.pdb in PDB format" % (n,root))
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# write a single PDB file
|
||||
@ -280,10 +290,10 @@ class pdbfile:
|
||||
if self.atomlines.has_key(id):
|
||||
(begin,end) = self.atomlines[id]
|
||||
line = "%s%8.3f%8.3f%8.3f%s" % (begin,atom[2],atom[3],atom[4],end)
|
||||
print >>f,line,
|
||||
print(line,file=f,end='')
|
||||
else:
|
||||
for atom in atoms:
|
||||
begin = "ATOM %6d %2d R00 1 " % (atom[0],atom[1])
|
||||
middle = "%8.3f%8.3f%8.3f" % (atom[2],atom[3],atom[4])
|
||||
end = " 1.00 0.00 NONE"
|
||||
print >>f,begin+middle+end
|
||||
print(begin+middle+end,file=f)
|
||||
|
||||
Reference in New Issue
Block a user