PSCF v1.3.3
r1d/misc/FieldIo.cpp
1/*
2* PSCF - Polymer Self-Consistent Field
3*
4* Copyright 2015 - 2025, The Regents of the University of Minnesota
5* Distributed under the terms of the GNU General Public License.
6*/
7
8#include "FieldIo.h"
9
10#include <r1d/domain/Domain.h>
11#include <r1d/solvers/Mixture.h>
12#include <r1d/solvers/Polymer.h>
13#include <r1d/solvers/Solvent.h>
14#include <r1d/solvers/Block.h>
15
16#include <pscf/chem/Vertex.h>
17#include <pscf/floryHuggins/Clump.h>
18
19#include <util/misc/FileMaster.h>
20#include <util/format/Str.h>
21#include <util/format/Int.h>
22#include <util/format/Dbl.h>
23
24#include <string>
25
26namespace Pscf {
27namespace R1d
28{
29
30 using namespace Util;
31
32 /*
33 * Constructor.
34 */
37
38 /*
39 * Destructor.
40 */
43
44
45 void FieldIo::associate(Domain const & domain,
46 FileMaster const & fileMaster)
47 {
48 domainPtr_ = &domain;
49 fileMasterPtr_ = &fileMaster;
50 }
51
53 std::string const & filename)
54 {
55 std::ifstream in;
56 fileMaster().openInputFile(filename, in);
57 readFields(fields, in);
58 in.close();
59 }
60
61 void FieldIo::readFields(DArray<Field>& fields, std::istream& in)
62 {
63 // Read grid dimensions
64 std::string label;
65 in >> label;
66 UTIL_CHECK(label == "nx");
67 int nx;
68 in >> nx;
69 UTIL_CHECK(nx > 0);
70 UTIL_CHECK(nx == domain().nx());
71 in >> label;
72 UTIL_CHECK (label == "nm");
73 int nm;
74 in >> nm;
75 UTIL_CHECK(nm > 0);
76
77 // Check dimensions of fields array
78 UTIL_CHECK(nm == fields.capacity());
79 for (int i = 0; i < nm; ++i) {
80 UTIL_CHECK(nx == fields[i].capacity());
81 }
82
83 // Read fields
84 int i, j, idum;
85 for (i = 0; i < nx; ++i) {
86 in >> idum;
87 UTIL_CHECK(idum == i);
88 for (j = 0; j < nm; ++j) {
89 in >> fields[j][i];
90 }
91 }
92 }
93
94 void FieldIo::writeField(Field const & field,
95 std::string const & filename, bool writeHeader) const
96 {
97 std::ofstream out;
98 fileMaster().openOutputFile(filename, out);
99 writeField(field, out, writeHeader);
100 out.close();
101 }
102
103 void FieldIo::writeField(Field const & field, std::ostream& out, bool writeHeader) const
104 {
105 int nx = field.capacity();
106 UTIL_CHECK(nx == domain().nx());
107
108 if (writeHeader){
109 out << "nx " << nx << std::endl;
110 }
111 // Write fields
112 int i;
113 for (i = 0; i < nx; ++i) {
114 out << " " << Dbl(field[i], 18, 11);
115 out << std::endl;
116 }
117 }
118
120 std::string const & filename, bool writeHeader)
121 const
122 {
123 std::ofstream out;
124 fileMaster().openOutputFile(filename, out);
125 writeFields(fields, out, writeHeader);
126 out.close();
127 }
128
130 std::ostream& out, bool writeHeader)
131 const
132 {
133 int nm = fields.capacity();
134 UTIL_CHECK(nm > 0);
135 int nx = fields[0].capacity();
136 UTIL_CHECK(nx == domain().nx());
137 if (nm > 1) {
138 for (int i = 0; i < nm; ++i) {
139 UTIL_CHECK(nx == fields[i].capacity());
140 }
141 }
142 if (writeHeader){
143 out << "nx " << nx << std::endl;
144 out << "nm " << nm << std::endl;
145 }
146 // Write fields
147 int i, j;
148 for (i = 0; i < nx; ++i) {
149 out << Int(i, 5);
150 for (j = 0; j < nm; ++j) {
151 out << " " << Dbl(fields[j][i], 18, 11);
152 }
153 out << std::endl;
154 }
155 }
156
157 void FieldIo::writeBlockCFields(Mixture const & mixture,
158 std::string const & filename) const
159 {
160 std::ofstream out;
161 fileMaster().openOutputFile(filename, out);
162 writeBlockCFields(mixture, out);
163 out.close();
164 }
165
166 /*
167 * Write the concentrations associated with all blocks.
168 */
169 void FieldIo::writeBlockCFields(Mixture const & mixture, std::ostream& out)
170 const
171 {
172 int nx = domain().nx(); // number grid points
173 int np = mixture.nPolymer(); // number of polymer species
174 int nb_tot = mixture.nBlock(); // number of blocks in all polymers
175 int ns = mixture.nSolvent(); // number of solvents
176
177 out << "nx " << nx << std::endl;
178 out << "n_block " << nb_tot << std::endl;
179 out << "n_solvent " << ns << std::endl;
180
181 int nb; // number of blocks per polymer
182 int i, j, k, l; // loop indices
183 double c;
184 for (i = 0; i < nx; ++i) {
185 out << Int(i, 5);
186 for (j = 0; j < np; ++j) {
187 nb = mixture.polymer(j).nBlock();
188 for (k = 0; k < nb; ++k) {
189 c = mixture.polymer(j).block(k).cField()[i];
190 out << " " << Dbl(c, 15, 8);
191 }
192 }
193 for (l = 0; l < ns; ++l) {
194 c = mixture.solvent(l).cField()[i];
195 out << " " << Dbl(c, 15, 8);
196 }
197 out << std::endl;
198 }
199 }
200
201 /*
202 * Write incoming q fields for a specified vertex.
203 */
204 void FieldIo::writeVertexQ(Mixture const & mixture,
205 int polymerId, int vertexId,
206 std::string const & filename) const
207 {
208 std::ofstream out;
209 fileMaster().openOutputFile(filename, out);
210 writeVertexQ(mixture, polymerId, vertexId, out);
211 out.close();
212 }
213
214 /*
215 * Write incoming q fields for a specified vertex.
216 */
217 void FieldIo::writeVertexQ(Mixture const & mixture,
218 int polymerId, int vertexId,
219 std::ostream& out) const
220 {
221 UTIL_CHECK(polymerId >= 0);
222 UTIL_CHECK(polymerId < mixture.nPolymer());
223 Polymer const & polymer = mixture.polymer(polymerId);
224 UTIL_CHECK(vertexId >= 0);
225 UTIL_CHECK(vertexId <= polymer.nBlock());
226 Vertex const & vertex = polymer.vertex(vertexId);
227 int nb = vertex.size(); // number of attached blocks
228 int nx = domain().nx(); // number grid points
229
230 Pair<int> pId;
231 int bId;
232 int dId;
233 int i, j;
234 double c, product;
235 for (i = 0; i < nx; ++i) {
236 out << Int(i, 5);
237 product = 1.0;
238 for (j = 0; j < nb; ++j) {
239 pId = vertex.inPropagatorId(j);
240 bId = pId[0]; // blockId
241 UTIL_CHECK(bId >= 0);
242 dId = pId[1]; // directionId
243 UTIL_CHECK(dId >= 0);
244 UTIL_CHECK(dId <= 1);
245 c = polymer.propagator(bId, dId).tail()[i];
246 out << " " << Dbl(c, 15, 8);
247 product *= c;
248 }
249 out << " " << Dbl(product, 15, 8) << std::endl;
250 }
251
252 }
253
254 /*
255 * Interpolate fields onto new mesh, write result to output file
256 */
257 void FieldIo::remesh(DArray<Field> const & fields, int nx,
258 std::string const & filename)
259 {
260 std::ofstream out;
261 fileMaster().openOutputFile(filename, out);
262 remesh(fields, nx, out);
263 out.close();
264 }
265
266 /*
267 * Interpolate fields onto new mesh, write result to outputstream
268 */
269 void
270 FieldIo::remesh(DArray<Field> const & fields, int nx, std::ostream& out)
271 {
272 // Query and check dimensions of fields array
273 int nm = fields.capacity();
274 UTIL_CHECK(nm > 0);
275 for (int i = 0; i < nm; ++i) {
276 UTIL_CHECK(fields[i].capacity() == domain().nx());
277 }
278
279 // Output new grid dimensions
280 out << "nx " << nx << std::endl;
281 out << "nm " << nm << std::endl;
282
283 // Output first grid point
284 int i, j;
285 i = 0;
286 out << Int(i, 5);
287 for (j = 0; j < nm; ++j) {
288 out << " " << Dbl(fields[j][i]);
289 }
290 out << std::endl;
291
292 // Spacing for new grid
293 double dx = (domain().xMax() - domain().xMin())/double(nx-1);
294
295 // Variables used for interpolation
296 double y; // Grid coordinate in old grid
297 double fu; // fractional part of y
298 double fl; // 1.0 - fl
299 double w; // interpolated field value
300 int yi; // Truncated integer part of y
301
302 // Loop over intermediate points
303 for (i = 1; i < nx -1; ++i) {
304 y = dx*double(i)/domain().dx();
305 yi = y;
306 UTIL_CHECK(yi >= 0);
307 UTIL_CHECK(yi + 1 < domain().nx());
308 fu = y - double(yi);
309 fl = 1.0 - fu;
310
311 out << Int(i, 5);
312 for (j = 0; j < nm; ++j) {
313 w = fl*fields[j][yi] + fu*fields[j][yi+1];
314 out << " " << Dbl(w);
315 }
316 out << std::endl;
317 }
318
319 // Output last grid point
320 out << Int(nx - 1, 5);
321 i = domain().nx() - 1;
322 for (j = 0; j < nm; ++j) {
323 out << " " << Dbl(fields[j][i]);
324 }
325 out << std::endl;
326
327 }
328
329 void
330 FieldIo::extend(DArray<Field> const & fields, int m,
331 std::string const & filename)
332 {
333 std::ofstream out;
334 fileMaster().openOutputFile(filename, out);
335 extend(fields, m, out);
336 out.close();
337 }
338
339 /*
340 * Interpolate fields onto new mesh.
341 */
342 void
343 FieldIo::extend(DArray<Field> const & fields, int m, std::ostream& out)
344 {
345 // Query and check dimensions of fields array
346 int nm = fields.capacity();
347 UTIL_CHECK(nm > 0);
348 int nx = fields[0].capacity();
349 UTIL_CHECK(nx == domain().nx());
350 if (nm > 1) {
351 for (int i = 0; i < nm; ++i) {
352 UTIL_CHECK(nx == fields[i].capacity());
353 }
354 }
355
356 // Output new grid dimensions
357 out << "nx " << nx + m << std::endl;
358 out << "nm " << nm << std::endl;
359
360 // Loop over existing points;
361 int i, j;
362 for (i = 0; i < nx; ++i) {
363 out << Int(i, 5);
364 for (j = 0; j < nm; ++j) {
365 out << " " << Dbl(fields[j][i]);
366 }
367 out << std::endl;
368 }
369
370 // Add m new points, assign each the last value
371 for (i = nx; i < nx + m; ++i) {
372 out << Int(i, 5);
373 for (j = 0; j < nm; ++j) {
374 out << " " << Dbl(fields[j][nx-1]);
375 }
376 out << std::endl;
377 }
378
379 }
380
381} // namespace R1d
382} // namespace Pscf
int nPolymer() const
Get number of polymer species.
int nBlock() const
Get total number blocks among all polymer species.
int nSolvent() const
Get number of solvent (point particle) species.
SolventT & solvent(int id)
Get a solvent solver object.
PolymerT & polymer(int id)
Get a polymer solver object by non-const reference.
const Vertex & vertex(int id) const
Get a specified Vertex by const reference.
PT & propagator(int blockId, int directionId)
Get the propagator for a specific block and direction (non-const).
int nBlock() const
Number of blocks.
One-dimensional spatial domain and discretization grid.
void extend(DArray< Field > const &fields, int m, std::ostream &out)
Add points to the end of a field mesh and write to stream.
void writeField(Field const &field, std::ostream &out, bool writeHeader=true) const
Write a single field to an output stream.
void writeVertexQ(Mixture const &mixture, int polymerId, int vertexId, std::ostream &out) const
Write product of incoming q fields for one vertex to stream.
void associate(Domain const &domain, FileMaster const &fileMaster)
Get and store addresses of associated objects.
void remesh(DArray< Field > const &fields, int nx, std::ostream &out)
Interpolate an array of fields onto a new mesh and write to stream.
void writeFields(DArray< Field > const &fields, std::ostream &out, bool writeHeader=true) const
Write a set of fields, one per monomer type, to an output stream.
void writeBlockCFields(Mixture const &mixture, std::ostream &out) const
Write block concentration fields for all blocks to an output stream.
void readFields(DArray< Field > &fields, std::istream &in)
Read a set of fields, one per monomer type.
Solver and descriptor for a mixture of polymers and solvents.
Descriptor and solver for a block polymer species.
A junction or chain end in a block polymer.
Definition Vertex.h:31
int size() const
Get the number of attached blocks.
Definition Vertex.h:110
Pair< int > const & inPropagatorId(int i) const
Get the block and direction of an incoming propagator.
Definition Vertex.h:114
int capacity() const
Return allocated size.
Definition Array.h:159
Dynamically allocatable contiguous array template.
Definition DArray.h:32
Wrapper for a double precision number, for formatted ostream output.
Definition Dbl.h:40
A FileMaster manages input and output files for a simulation.
Definition FileMaster.h:143
Wrapper for an int, for formatted ostream output.
Definition Int.h:37
An array of exactly 2 objects.
Definition Pair.h:24
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition global.h:68
SCFT with real 1D fields.
PSCF package top-level namespace.
float product(float a, float b)
Product for float Data.
Definition product.h:22