2 # Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
3 # (HOOMD-blue) Open Source Software License Copyright 2008-2011 Ames Laboratory
4 # Iowa State University and The Regents of the University of Michigan All rights
7 # HOOMD-blue may contain modifications ("Contributions") provided, and to which
8 # copyright is held, by various Contributors who have granted The Regents of the
9 # University of Michigan the right to modify and/or distribute such Contributions.
11 # You may redistribute, use, and create derivate works of HOOMD-blue, in source
12 # and binary forms, provided you abide by the following conditions:
14 # * Redistributions of source code must retain the above copyright notice, this
15 # list of conditions, and the following disclaimer both in the code and
16 # prominently in any materials provided with the distribution.
18 # * Redistributions in binary form must reproduce the above copyright notice, this
19 # list of conditions, and the following disclaimer in the documentation and/or
20 # other materials provided with the distribution.
22 # * All publications and presentations based on HOOMD-blue, including any reports
23 # or published results obtained, in whole or in part, with HOOMD-blue, will
24 # acknowledge its use according to the terms posted at the time of submission on:
25 # http://codeblue.umich.edu/hoomd-blue/citations.html
27 # * Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
28 # http://codeblue.umich.edu/hoomd-blue/
30 # * Apart from the above required attributions, neither the name of the copyright
31 # holder nor the names of HOOMD-blue's contributors may be used to endorse or
32 # promote products derived from this software without specific prior written
37 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
38 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
39 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
40 # WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.
42 # IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
43 # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
44 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
46 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
47 # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
48 # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
51 # Maintainer: joaander / All Developers are free to add commands for new features
53 ## \package hoomd_script.external
54 # \brief Commands that create external forces on particles
56 # Apply an %external force to all particles in the simulation. This module organizes all external forces.
57 # As an example, a force derived from a %periodic potential can be used to induce a concentration modulation
60 from hoomd_script
import globals;
61 from hoomd_script
import force
;
63 from hoomd_script
import util
;
64 from hoomd_script
import init
;
65 from hoomd_script
import data
;
66 from hoomd_script
import tune
;
70 # \brief Defines external potential coefficients
71 # The coefficients for all %external force are specified using this class. Coefficients are specified per-particle
74 # There are two ways to set the coefficients for a particular %external %force.
75 # The first way is to save the %external %force in a variable and call set() directly.
76 # To see an example of this, see the documentation for the package.
78 # The second method is to build the coeff class first and then assign it to the
79 # %external %force. There are some advantages to this method in that you could specify a
80 # complicated set of %external %force coefficients in a separate python file and import it into
85 # my_coeffs = external.coeff();
86 # my_external_force.force_coeff.set('A', A=1.0, i=1, w=0.02, p=3)
87 # my_external_force.force_coeff.set('B', A=-1.0, i=1, w=0.02, p=3)
92 # \brief Initializes the class
94 # The main task to be performed during initialization is just to init some variables
95 # \param self Python required class instance variable
98 self
.default_coeff
= {}
102 # \brief Contains the vector of set values in a dictionary
104 ## \var default_coeff
106 # \brief default_coeff['coeff'] lists the default value for \a coeff, if it is set
109 # \brief Sets a default value for a given coefficient
111 # \param name Name of the coefficient to for which to set the default
112 # \param value Default value to set
114 # Some coefficients have reasonable default values and the user should not be burdened with typing them in
115 # all the time. set_default_coeff() sets
116 def set_default_coeff(self
, name
, value
):
117 self
.default_coeff
[name
] = value
;
119 ## Sets parameters for one particle type
120 # \param type Type of particle
121 # \param coeff Named coefficients (see below for examples)
123 # Calling set() results in one or more parameters being set for a particle type. Types are identified
124 # by name, and parameters are also added by name. Which parameters you need to specify depends on the %external
125 # %force you are setting these coefficients for, see the corresponding documentation.
127 # All possible particle types as defined in the simulation box must be specified before executing run().
128 # You will receive an error if you fail to do so. It is not an error, however, to specify coefficients for
129 # particle types that do not exist in the simulation. This can be useful in defining a %force field for many
130 # different types of particles even when some simulations only include a subset.
132 # To set the same coefficients between many particle types, provide a list of type names instead of a single
133 # one. All types in the list will be set to the same parameters. A convenient wildcard that lists all types
134 # of particles in the simulation can be gotten from a saved \c system from the init command.
138 # coeff.set('A', A=1.0, i=1, w=0.02, p=3)
139 # coeff.set('B', A=-1.0, i=1, w=0.02, p=3)
140 # coeff.set(['A','B'], i=1, w=0.02, p=3)
143 # \note Single parameters can be updated. If both epsilon and sigma have already been set for a particle type,
144 # then executing coeff.set('A', A =1.0) will %update the value of A and leave the other parameters as they
145 # were previously set.
147 def set(self
, type, **coeffs
):
148 util
.print_status_line();
151 if isinstance(type, str):
155 self
.set_single(typei
, coeffs
);
158 # \brief Sets a single parameter
159 def set_single(self
, type, coeffs
):
160 # create the type identifier if it hasn't been created yet
161 if (not type in self
.values
):
162 self
.values
[type] = {};
164 # update each of the values provided
166 globals.msg
.error("No coefficents specified\n");
167 for name
, val
in coeffs
.items():
168 self
.values
[type][name
] = val
;
170 # set the default values
171 for name
, val
in self
.default_coeff
.items():
172 # don't override a coeff if it is already set
173 if not name
in self
.values
[type]:
174 self
.values
[type][name
] = val
;
177 # \brief Verifies that all values are set
179 # \param self Python required self variable
180 # \param required_coeffs list of required variables
182 # This can only be run after the system has been initialized
183 def verify(self
, required_coeffs
):
184 # first, check that the system has been initialized
185 if not init
.is_initialized():
186 globals.msg
.error("Cannot verify force coefficients before initialization\n");
187 raise RuntimeError('Error verifying force coefficients');
189 # get a list of types from the particle data
190 ntypes
= globals.system_definition
.getParticleData().getNTypes();
192 for i
in range(0,ntypes
):
193 type_list
.append(globals.system_definition
.getParticleData().getNameByType(i
));
196 # loop over all possible types and verify that all required variables are set
197 for i
in range(0,ntypes
):
200 # verify that all required values are set by counting the matches
202 for coeff_name
in self
.values
[type].keys():
203 if not coeff_name
in required_coeffs
:
204 globals.msg
.notice(3, "Possible typo? Force coeff " + str(coeff_name
) + " is specified for type " + str(type) +\
205 ", but is not used by the external force");
209 if count
!= len(required_coeffs
):
210 globals.msg
.error("Particle type", type, "is missing required coefficients\n");
216 # \brief Gets the value of a single %external %force coefficient
218 # \param type Name of particle type
219 # \param coeff_name Coefficient to get
220 def get(self
, type, coeff_name
):
221 if type not in self
.values
:
222 globals.msg
.error("Bug detected in external.coeff. Please report\n");
223 raise RuntimeError("Error setting external coeff");
225 return self
.values
[type][coeff_name
];
228 # \brief Base class for external forces
230 # An external_force in hoomd_script reflects a PotentialExternal in c++. It is responsible
231 # for all high-level management that happens behind the scenes for hoomd_script
232 # writers. 1) The instance of the c++ external force itself is tracked and added to the
233 # System 2) methods are provided for disabling the force from being added to the
234 # net force on each particle
235 class _external_force(force
._force
):
237 # \brief Constructs the external force
239 # \param name name of the external force instance
241 # Initializes the cpp_force to None.
242 # If specified, assigns a name to the instance
243 # Assigns a name to the force in force_name;
244 def __init__(self
, name
=None):
245 # initialize the base class
246 force
._force
.__init
__(self
, name
);
248 self
.cpp_force
= None;
250 # setup the coefficient vector
251 self
.force_coeff
= coeff();
256 # create force data iterator
257 self
.external_forces
= data
.force_data(self
);
259 def update_coeffs(self
):
260 coeff_list
= self
.required_coeffs
;
261 # check that the force coefficients are valid
262 if not self
.force_coeff
.verify(coeff_list
):
263 globals.msg
.error("Not all force coefficients are set\n");
264 raise RuntimeError("Error updating force coefficients");
267 ntypes
= globals.system_definition
.getParticleData().getNTypes();
269 for i
in range(0,ntypes
):
270 type_list
.append(globals.system_definition
.getParticleData().getNameByType(i
));
272 for i
in range(0,ntypes
):
273 # build a dict of the coeffs to pass to proces_coeff
275 for name
in coeff_list
:
276 coeff_dict
[name
] = self
.force_coeff
.get(type_list
[i
], name
);
278 param
= self
.process_coeff(coeff_dict
);
279 self
.cpp_force
.setParams(i
, param
);
281 ## One-dimension periodic potential
283 # The command %periodic specifies that an external %force should be
284 # added to every particle in the simulation to induce a periodic modulation
285 # in the particle concentration. The force parameters can be set on a per-particle
286 # type-basis. The potential can e.g. be used to induce an ordered phase in a block-copolymer melt.
288 # The external potential \f$V(\vec{r}) \f$ is implemented using the following formula:
291 # V(\vec{r}) = A * \tanh\left[\frac{1}{2 \pi p w} \cos\left(p \vec{b}_i\cdot\vec{r}\right)\right]
294 # where \f$A\f$ is the ordering parameter, \f$\vec{b}_i\f$ is the reciprocal lattice vector direction
295 # \f$i=0..2\f$, \f$p\f$ the periodicity and \f$w\f$ the interface width
296 # (relative to the distance \f$2\pi/|\mathbf{b_i}|\f$ between planes in the \f$i\f$-direction).
297 # The modulation is one-dimensional. It extends along the lattice vector \f$\mathbf{a}_i\f$ of the
301 class periodic(_external_force
):
302 ## Apply a force derived from a %periodic potential to all particles
306 # # Apply a periodic composition modulation along the first lattice vector
307 # periodic = external.periodic()
308 # periodic.force_coeff.set('A', A=1.0, i=0, w=0.02, p=3)
309 # periodic.force_coeff.set('B', A=-1.0, i=0, w=0.02, p=3)
312 def __init__(self
, name
=""):
313 util
.print_status_line();
315 # initialize the base class
316 _external_force
.__init
__(self
, name
);
318 # create the c++ mirror class
319 if not globals.exec_conf
.isCUDAEnabled():
320 self
.cpp_force
= hoomd
.PotentialExternalPeriodic(globals.system_definition
,self
.name
);
322 self
.cpp_force
= hoomd
.PotentialExternalPeriodicGPU(globals.system_definition
,self
.name
);
324 globals.system
.addCompute(self
.cpp_force
, self
.force_name
);
326 # setup the coefficient options
327 self
.required_coeffs
= ['A','i','w','p'];
329 def process_coeff(self
, coeff
):
335 return hoomd
.make_scalar4(hoomd
.int_as_scalar(i
), A
, w
, hoomd
.int_as_scalar(p
));