PSCF v1.3
ScftReal.tpp
1#ifndef PRDC_SCFT_REAL_TPP
2#define PRDC_SCFT_REAL_TPP
3
4/*
5* PSCF - Polymer Self-Consistent Field
6*
7* Copyright 2015 - 2025, The Regents of the University of Minnesota
8* Distributed under the terms of the GNU General Public License.
9*/
10
11#include "ScftReal.h"
12
13#include <pscf/mesh/Mesh.h>
14#include <pscf/chem/PolymerSpecies.h>
15#include <pscf/chem/SolventSpecies.h>
16#include <util/format/Int.h>
17#include <util/format/Dbl.h>
18
19namespace Pscf {
20namespace Prdc {
21
22 using namespace Util;
23 using namespace Pscf::Prdc;
24 using namespace Pscf::Prdc::Cpu;
25
26 /*
27 * Constructor.
28 */
29 template <int D, class ST>
32 fHelmholtz_(0.0),
33 fIdeal_(0.0),
34 fInter_(0.0),
35 fExt_(0.0),
36 pressure_(0.0),
37 hasData_(false)
38 {}
39
40 /*
41 * Destructor.
42 */
43 template <int D, class ST>
46
47 /*
48 * Compute Helmholtz free energy and pressure.
49 */
50 template <int D, class ST>
52 {
53 if (hasData_) return;
54
55 UTIL_CHECK(w().hasData());
56 UTIL_CHECK(c().hasData());
57
58 int nm = mixture().nMonomer(); // number of monomer types
59 int np = mixture().nPolymer(); // number of polymer species
60 int ns = mixture().nSolvent(); // number of solvent species
61
62 // Initialize all free energy contributions to zero
63 fHelmholtz_ = 0.0;
64 fIdeal_ = 0.0;
65 fInter_ = 0.0;
66 fExt_ = 0.0;
67
68 double phi, mu;
69
70 // Compute polymer ideal gas contributions to fIdeal_
71 if (np > 0) {
72 PolymerSpecies const * polymerPtr;
73 double length;
74 for (int i = 0; i < np; ++i) {
75 polymerPtr = &mixture().polymerSpecies(i);
76 phi = polymerPtr->phi();
77 mu = polymerPtr->mu();
79 length = polymerPtr->length();
80 } else {
81 length = (double) polymerPtr->nBead();
82 }
83 if (phi > 1.0E-08) {
84 fIdeal_ += phi*( mu - 1.0 ) / length;
85 // Recall: mu = ln(phi/q)
86 }
87 }
88 }
89
90 // Compute solvent ideal gas contributions to fIdeal_
91 if (ns > 0) {
92 SolventSpecies const * solventPtr;
93 double size;
94 for (int i = 0; i < ns; ++i) {
95 solventPtr = &mixture().solventSpecies(i);
96 phi = solventPtr->phi();
97 mu = solventPtr->mu();
98 size = solventPtr->size();
99 if (phi > 1.0E-08) {
100 fIdeal_ += phi*( mu - 1.0 )/size;
101 }
102 }
103 }
104
105 // Volume integrals with a mask: If the system has a mask, then the
106 // volume that should be used in calculating free energy/pressure
107 // is the volume available to the material, not the total unit cell
108 // volume. We thus divide all terms that involve integrating over
109 // the unit cell volume by quantity mask().phiTot(), which is the
110 // volume fraction of the unit cell that is occupied by material.
111 // This properly scales them to the correct value. fExt_, fInter_,
112 // and the Legendre transform component of fIdeal_ all require
113 // this scaling. If no mask is present, mask().phiTot() = 1 and no
114 // scaling occurs.
115 const double phiTot = mask().phiTot();
116
117 // Compute Legendre transform subtraction from fIdeal_
118 double temp = 0.0;
119 if (w().isSymmetric()) {
120 // Use expansion in symmetry-adapted orthonormal basis
121 UTIL_CHECK(w().isAllocatedBasis());
122 UTIL_CHECK(c().isAllocatedBasis());
123 const int nBasis = domain().basis().nBasis();
124 for (int i = 0; i < nm; ++i) {
125 for (int k = 0; k < nBasis; ++k) {
126 temp -= w().basis(i)[k] * c().basis(i)[k];
127 }
128 }
129 } else {
130 // Use summation over grid points
131 const int meshSize = domain().mesh().size();
132 for (int i = 0; i < nm; ++i) {
133 temp -= innerProduct(w().rgrid(i), c().rgrid(i));
134 #if 0
135 for (int k = 0; k < meshSize; ++k) {
136 temp -= w().rgrid(i)[k] * c().rgrid(i)[k];
137 }
138 #endif
139 }
140 temp /= double(meshSize);
141 }
142 temp /= phiTot;
143 fIdeal_ += temp;
144 fHelmholtz_ += fIdeal_;
145
146 // Compute contribution from external fields, if they exist
147 if (h().hasData()) {
148 if (w().isSymmetric() && h().isSymmetric()) {
149 // Use expansion in symmetry-adapted orthonormal basis
150 UTIL_CHECK(h().isAllocatedBasis());
151 UTIL_CHECK(c().isAllocatedBasis());
152 const int nBasis = domain().basis().nBasis();
153 for (int i = 0; i < nm; ++i) {
154 for (int k = 0; k < nBasis; ++k) {
155 fExt_ += h().basis(i)[k] * c().basis(i)[k];
156 }
157 }
158 } else {
159 // Use summation over grid points
160 const int meshSize = domain().mesh().size();
161 for (int i = 0; i < nm; ++i) {
162 fExt_ += innerProduct(h().rgrid(i), c().rgrid(i));
163 #if 0
164 for (int k = 0; k < meshSize; ++k) {
165 fExt_ += h().rgrid(i)[k] * c().rgrid(i)[k];
166 }
167 #endif
168 }
169 fExt_ /= double(meshSize);
170 }
171 fExt_ /= phiTot;
172 fHelmholtz_ += fExt_;
173 }
174
175 // Compute excess interaction free energy [ phi^{T}*chi*phi/2 ]
176 if (w().isSymmetric()) {
177 // Use basis expansion
178 UTIL_CHECK(c().isAllocatedBasis());
179 const int nBasis = domain().basis().nBasis();
180 double temp;
181 for (int i = 0; i < nm; ++i) {
182 for (int j = i; j < nm; ++j) {
183 const double chi = interaction().chi(i,j);
184 if (std::abs(chi) > 1.0E-9) {
185 temp = 0.0;
186 for (int k = 0; k < nBasis; ++k) {
187 temp += c().basis(i)[k] * c().basis(j)[k];
188 }
189 if (i == j) {
190 fInter_ += 0.5*chi*temp;
191 } else {
192 fInter_ += chi*temp;
193 }
194 }
195 }
196 }
197 } else {
198 // Use r-grid data
199 const int meshSize = domain().mesh().size();
200 double temp;
201 for (int i = 0; i < nm; ++i) {
202 for (int j = i; j < nm; ++j) {
203 const double chi = interaction().chi(i,j);
204 if (std::abs(chi) > 1.0E-9) {
205 temp = innerProduct(c().rgrid(i), c().rgrid(j));
206 #if 0
207 temp = 0.0;
208 for (int k = 0; k < meshSize; ++k) {
209 temp += c().rgrid(i)[k] * c().rgrid(j)[k];
210 }
211 #endif
212 if (i == j) {
213 fInter_ += 0.5*chi*temp;
214 } else {
215 fInter_ += chi*temp;
216 }
217 }
218 }
219 }
220 fInter_ /= double(meshSize);
221 }
222 fInter_ /= phiTot;
223 fHelmholtz_ += fInter_;
224
225 // Initialize pressure (-1 x grand-canonical free energy / monomer)
226 pressure_ = -fHelmholtz_;
227
228 // Polymer chemical potential corrections to pressure
229 if (np > 0) {
230 PolymerSpecies const * polymerPtr;
231 double length;
232 for (int i = 0; i < np; ++i) {
233 polymerPtr = &mixture().polymerSpecies(i);
234 phi = polymerPtr->phi();
235 mu = polymerPtr->mu();
237 length = polymerPtr->length();
238 } else {
239 length = (double) polymerPtr->nBead();
240 }
241 if (phi > 1.0E-08) {
242 pressure_ += mu * phi / length;
243 }
244 }
245 }
246
247 // Solvent corrections to pressure
248 if (ns > 0) {
249 SolventSpecies const * solventPtr;
250 double size;
251 for (int i = 0; i < ns; ++i) {
252 solventPtr = &mixture().solventSpecies(i);
253 phi = solventPtr->phi();
254 mu = solventPtr->mu();
255 size = solventPtr->size();
256 if (phi > 1.0E-08) {
257 pressure_ += mu * phi / size;
258 }
259 }
260 }
261
262 hasData_ = true;
263 }
264
265 /*
266 * Clear stored values of thermodynamic properties.
267 */
268 template <int D, class ST>
270 { hasData_ = false; }
271
272 /*
273 * Write thermodynamic properties to file.
274 */
275 template <int D, class ST>
276 void ScftReal<D, ST>::write(std::ostream& out)
277 {
278 if (!hasData_) {
279 compute();
280 }
281
282 out << std::endl;
283 out << "fHelmholtz " << Dbl(fHelmholtz(), 18, 11) << std::endl;
284 out << "pressure " << Dbl(pressure(), 18, 11) << std::endl;
285 out << std::endl;
286 out << "fIdeal " << Dbl(fIdeal_, 18, 11) << std::endl;
287 out << "fInter " << Dbl(fInter_, 18, 11) << std::endl;
288 if (h().hasData()) {
289 out << "fExt " << Dbl(fExt_, 18, 11) << std::endl;
290 }
291 out << std::endl;
292
293 int np = mixture().nPolymer();
294 int ns = mixture().nSolvent();
295
296 if (np > 0) {
297 out << "polymers:" << std::endl;
298 out << " "
299 << " phi "
300 << " mu "
301 << std::endl;
302 for (int i = 0; i < np; ++i) {
303 out << Int(i, 5)
304 << " " << Dbl(mixture().polymer(i).phi(),18, 11)
305 << " " << Dbl(mixture().polymer(i).mu(), 18, 11)
306 << std::endl;
307 }
308 out << std::endl;
309 }
310
311 if (ns > 0) {
312 out << "solvents:" << std::endl;
313 out << " "
314 << " phi "
315 << " mu "
316 << std::endl;
317 for (int i = 0; i < ns; ++i) {
318 out << Int(i, 5)
319 << " " << Dbl(mixture().solvent(i).phi(),18, 11)
320 << " " << Dbl(mixture().solvent(i).mu(), 18, 11)
321 << std::endl;
322 }
323 out << std::endl;
324 }
325
326 out << "cellParams:" << std::endl;
327 for (int i = 0; i < domain().unitCell().nParameter(); ++i) {
328 out << Int(i, 5)
329 << " "
330 << Dbl(domain().unitCell().parameter(i), 18, 11)
331 << std::endl;
332 }
333 out << std::endl;
334 }
335
336} // namespace Prdc
337} // namespace Pscf
338#endif
Descriptor for a linear or acyclic branched block polymer.
int nBead() const
Total number of beads in the polymer (bead model).
double length() const
Sum of the lengths of all blocks in the polymer (thread model).
virtual ~ScftReal()
Destructor.
Definition ScftReal.tpp:44
typename Base::SystemT SystemT
Parent System type name alias.
Definition ScftReal.h:33
DomainT const & domain() const
Get the Domain.
virtual double innerProduct(FieldT const &A, FieldT const &B) const
Inner product of two fields.
Definition ScftReal.h:169
double fHelmholtz() const
Get total Helmholtz free energy per monomer / kT.
Definition ScftReal.h:219
SystemT const & system() const
Get the associated System.
void clear()
Clear all thermodynamic data.
Definition ScftReal.tpp:269
bool hasData() const
Have free energies and pressure been computed?
Definition ScftReal.h:259
MaskT const & mask() const
Get the mask.
ScftReal(SystemT const &system)
Constructor.
Definition ScftReal.tpp:30
void write(std::ostream &out)
Write SCFT thermodynamic properties to a file.
Definition ScftReal.tpp:276
WFieldContainerT const & w() const
Get the chemical potential (w) field container.
MixtureT const & mixture() const
Get the Mixture.
WFieldContainerT const & h() const
Get the external potential (h) field container.
double pressure() const
Get the precomputed pressure times monomer volume / kT.
Definition ScftReal.h:251
CFieldContainerT const & c() const
Get the concentration (c) field container.
InteractionT const & interaction() const
Get the Interaction.
void compute()
Compute SCFT free energy density and pressure for current fields.
Definition ScftReal.tpp:51
SystemConstRefReal()
Default constructor.
Descriptor for a solvent species.
double size() const
Get the size (number of monomers) in this solvent.
double phi() const
Get the overall volume fraction for this species.
Definition Species.h:149
double mu() const
Get the chemical potential for this species (units kT=1).
Definition Species.h:155
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
Wrapper for an int, for formatted ostream output.
Definition Int.h:37
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
bool isThread()
Is the thread model in use ?
Fields and FFTs for periodic boundary conditions (CPU)
Definition CField.cpp:12
Periodic fields and crystallography.
Definition CField.cpp:11
PSCF package top-level namespace.
Definition param_pc.dox:1