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