001/* 002 * GTCGeomReader -- A class that can read GridTool formatted restart file. 003 * 004 * Copyright (C) 2013-2025, Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom.reader; 023 024import geomss.geom.*; 025import geomss.geom.nurbs.*; 026import jahuwaldt.js.param.Parameter; 027import jahuwaldt.js.util.TextTokenizer; 028import jahuwaldt.tools.math.MathTools; 029import java.io.*; 030import java.nio.charset.StandardCharsets; 031import java.text.MessageFormat; 032import java.text.SimpleDateFormat; 033import java.util.Date; 034import java.util.List; 035import java.util.Locale; 036import static java.util.Objects.isNull; 037import static java.util.Objects.nonNull; 038import static java.util.Objects.requireNonNull; 039import javax.measure.quantity.Length; 040import javax.measure.unit.Unit; 041import javolution.context.StackContext; 042import javolution.text.Text; 043import javolution.text.TypeFormat; 044import javolution.util.FastTable; 045 046/** 047 * A {@link GeomReader} for reading geometry from a GridTool restart file. GridTool is a 048 * NASA developed grid/geometry program that is a part of the NASA TetrUSS CFD system. 049 * More information can be found here: <a href="http://geolab.larc.nasa.gov/GridTool/"> 050 * http://geolab.larc.nasa.gov/GridTool/</a> 051 * 052 * <p> Modified by: Joseph A. Huwaldt </p> 053 * 054 * @author Joseph A. Huwaldt, Date: December 26, 2013 055 * @version February 17, 2025 056 */ 057public class GTCGeomReader extends AbstractGeomReader { 058 059 // Debug output flag. 060 //private static final boolean DEBUG = true; 061 062 // A brief description of the data read by this reaader. 063 private static final String DESCRIPTION = RESOURCES.getString("gtcDescription"); 064 065 // The preferred file extension for files of this reader's type. 066 public static final String EXTENSION = "rst"; 067 068 // 10 kilobytes 069 private static final int TENK = 10240; 070 071 /** 072 * Returns a string representation of the object. This will return a brief description 073 * of the format read by this reader. 074 * 075 * @return A brief description of the format read by this reader. 076 */ 077 @Override 078 public String toString() { 079 return DESCRIPTION; 080 } 081 082 /** 083 * Returns the preferred file extension (not including the ".") for files of this 084 * GeomReader's type. 085 * 086 * @return The preferred file extension for files of this readers type. 087 */ 088 @Override 089 public String getExtension() { 090 return EXTENSION; 091 } 092 093 /** 094 * This method always returns <code>false</code> as GTC restart files do not encode 095 * the units that are being used. You must call <code>setFileUnits</code> to set the 096 * units being used before reading from a file of this format, otherwise 097 * default system units will be assumed. 098 * 099 * @return this implementation always returns false 100 * @see #setFileUnits(javax.measure.unit.Unit) 101 */ 102 @Override 103 public boolean isUnitAware() { 104 return false; 105 } 106 107 /** 108 * Method that determines if this reader can read geometry from the specified input 109 * file. 110 * 111 * @param inputFile The input file containing the geometry to be read in. May not be 112 * null. 113 * @return GeomReader.NO if the file format is not recognized by this reader. 114 * GeomReader.YES if the file format is definitely recognized by this reader. 115 * GeomReader.MAYBE if the file format might be readable by this reader, but that 116 * can't easily be determined without actually reading the file. 117 * @throws java.io.IOException If there is a problem reading from the specified file. 118 */ 119 @Override 120 public int canReadData(File inputFile) throws IOException { 121 requireNonNull(inputFile); 122 123 // GTC files are required to be in ASCII with U.S. style number formatting. 124 // Get the default locale and save it. Then change the default locale to US. 125 Locale defLocale = Locale.getDefault(); 126 Locale.setDefault(Locale.US); 127 128 int response = NO; 129 // Create an input stream from the file. 130 try (FileInputStream input = new FileInputStream(inputFile)) { 131 132 // Create a buffer to hold the data read in from the file. 133 byte[] buffer = new byte[TENK]; 134 135 // Read in up to 10k worth of data. 136 int byteCount = input.read(buffer, 0, TENK); 137 if (byteCount < 0) 138 return NO; 139 140 // Turn the buffer into a (potentially 10k long) string. 141 String str = new String(buffer, 0, byteCount); 142 143 // Convert the string into a line number reader. 144 LineNumberReader lnr = new LineNumberReader(new StringReader(str)); 145 146 // The 5th line should start with "Version X.X" where X.X is >= 4.1 147 lnr.readLine(); 148 lnr.readLine(); 149 lnr.readLine(); 150 lnr.readLine(); 151 String aLine = lnr.readLine(); 152 TextTokenizer tokenizer = TextTokenizer.valueOf(aLine, " "); 153 Text token = tokenizer.nextToken(); 154 if (token.equals(Text.valueOf("Version"))) { 155 token = tokenizer.nextToken(); 156 String[] parts = token.toString().split("\\."); 157 if (parts.length > 2) 158 return NO; 159 double major = TypeFormat.parseInt(parts[0]); 160 double minor = TypeFormat.parseInt(parts[1]); 161 double ver = major + minor / 10; 162 if (ver == 4.1) 163 return YES; 164 } 165 166 } catch (Throwable e) { 167 // Any problem reading is a failure. 168 response = NO; 169 170 } finally { 171 // Restore the default locale. 172 Locale.setDefault(defLocale); 173 } 174 175 return response; 176 } 177 178 /** 179 * <p> 180 * Reads in a GridTool restart file from the specified input file and returns a 181 * {@link GeometryList} object that contains the geometry from the file. The output 182 * list contains 4 sub-lists: SURFACES, CURVES, PATCHES and SOURCES. Empty lists are 183 * included if any of these entities is not included in the file. 184 * </p> 185 * 186 * The top-level list has the following data stored in the user data using the 187 * following keys: 188 * <UL> 189 * <LI> GTC_Version -- The GTC version code. Must be ≥ 1.4 to be read by this reader. 190 * <LI> GTC_Tol -- The gobal geometry tolerance used. 191 * <LI> GTC_Name -- The GTC model name. 192 * </UL> 193 * 194 * Each sub-list is defined as follows: 195 * <DL> 196 * <DT> SURFACES 197 * <DD> All the NURBS surfaces and NURBS curves in the geometry. Each surface & curve 198 * has stored in its user data the minimum and maximum parameter ranges for the 199 * original geometry using the GTC_U0, GTC_U1, GTC_V0, and GTC_V1 keys. All NURBS 200 * surfaces & curves in GeomSS have parameter ranges from 0 to 1, but other geometry 201 * systems allow different parameter ranges. In addition, the IGES type code for the 202 * geometry is stored with the GTC_Type key and the family name is stored with the 203 * GTC_Family key. 204 * <DT> CURVES 205 * <DD> This list contains PointString objects (which are lists of points) for each 206 * "curve". Each point stored in each point string is either free (in X,Y,Z) or 207 * subranged onto a NURBS surface or curve. 208 * <DT> PATCHES 209 * <DD> Each patch in the PATCHES list contains two lists, one containing either 210 * nothing or the NURBS surface associated with the patch and one containing a list of 211 * edge curves (PointStrings) for the patch. Each patch has user data stored with it 212 * using the following keys: 213 * <UL> 214 * <LI> GTC_Type -- The patch type code 215 * <LI> GTC_BCType -- The boundary condition type code 216 * <LI> GTC_Family -- The family name 217 * <LI> GTC_ValidPatch -- A patch validity flag 218 * <LI> GTC_D3MNumber -- The d3m number for the patch 219 * <LI> GTC_EdgeSideIndexes -- A list of side indexes for each edge (which side does 220 * each edge belong to) 221 * <LI> GTC_EdgeReversedFlags -- A list of flags indicating if each edge is reversed or not. 222 * </UL> 223 * <DT> SOURCES 224 * <DD> The sources for the "background grid" are stored in this list as PointString 225 * objects. Each PointString has two points for the ends of the source (though the 2nd 226 * one is sometimes not required, it is always there). As with curves, the points can 227 * either be in free space (X,Y,Z) or subranged onto a NURBS curve or surface. The 228 * global source list has the following user data keys associated with it: 229 * <UL> 230 * <LI> GTC_Delta1, GTC_Rate1, & GTC_Rate2 -- Global boundary layer growth parameters. 231 * <LI> GTC_NLayers -- The number of layers in the boundary layer grid 232 * <LI> GTC_NIterations -- The number of refinement iterations for the boundary layer grid. 233 * <LI> GTC_Stretching -- Global flags indicating if there is cell stretching in this grid 234 * <LI> GTC_ViscousLayers -- Global flag indicating if viscous layers should be grown or not. 235 * </UL> 236 * Each source in the SOURCES list has the following user data keys associated with it: 237 * <UL> 238 * <LI> GTC_SourceType -- The type code for the source. 239 * <LI> GTC_Family -- The family name 240 * <LI> GTC_StretchingDir -- The stretching direction code. 241 * <LI> GTC_an, GTC_bn, and GTC_Alpha -- Source strength parameters 242 * </UL> 243 * Finally, each point in each source has the following keys stored in it's user data: 244 * <UL> 245 * <LI> GTC_s, GTC_S -- Source strength information 246 * <LI> GTC_ro, GTC_ri -- Outer and inner radii for volume sources if required by the source type. 247 * <LI> GTC_Delta1, GTC_Rate1, GTC_Rate2 -- Values to override the global boundary layer 248 * grown parameters. 249 * </UL> 250 * </DL> 251 * <p> 252 * WARNING: This file format is not unit aware. You must set the units 253 * to be used by calling "setFileUnits()" before calling this method! 254 * </p> 255 * 256 * @param inputFile The input file containing the geometry to be read in. May not be 257 * null. 258 * @return A {@link GeometryList} object containing the geometry read in from the 259 * file. If the file has no geometry in it, then this list will have no 260 * components in it (will have a size() of zero). 261 * @throws IOException If there is a problem reading the specified file. 262 * @see #setFileUnits(javax.measure.unit.Unit) 263 */ 264 @Override 265 @SuppressWarnings("null") 266 public GeometryList read(File inputFile) throws IOException { 267 requireNonNull(inputFile); 268 _warnings.clear(); 269 270 // Create an empty geometry list. 271 GeometryList output = GeomList.newInstance(); 272 GeomList surfaces = GeomList.newInstance(); 273 surfaces.setName("SURFACES"); 274 output.add(surfaces); 275 GeomList<PointString> curves = GeomList.newInstance(); 276 curves.setName("CURVES"); 277 output.add(curves); 278 GeomList patches = GeomList.newInstance(); 279 patches.setName("PATCHES"); 280 output.add(patches); 281 GeomList sources = GeomList.newInstance(); 282 sources.setName("SOURCES"); 283 output.add(sources); 284 285 Unit<Length> unit = getFileUnits(); 286 TextTokenizer tokenizer = TextTokenizer.newInstance(); 287 tokenizer.setDelimiters(" "); 288 289 // GTC files are required to be in ASCII with U.S. style number formatting. 290 // Get the default locale and save it. Then change the default locale to US. 291 Locale defLocale = Locale.getDefault(); 292 Locale.setDefault(Locale.US); 293 294 // Create a reader to access the ASCII file. 295 try (LineNumberReader reader = new LineNumberReader(new FileReader(inputFile))) { 296 try { 297 // The 1st 4 lines are comments. 298 for (int i = 0; i < 4; ++i) 299 readLine(reader); 300 301 // Get the GTC version information. 302 String aLine = readLine(reader); 303 tokenizer.setText(aLine); 304 tokenizer.nextToken(); 305 String version = tokenizer.nextToken().toString(); 306 output.putUserData("GTC_Version", version); 307 308 // Read in the global tolerance parameter. 309 Text token = tokenizer.nextToken(); 310 double tol = TypeFormat.parseDouble(token); 311 output.putUserData("GTC_Tol", Parameter.valueOf(tol, unit)); 312 313 // Read in the name of the model. 314 String name = readLine(reader).trim(); 315 output.setName(name); 316 output.putUserData("GTC_Name", name); 317 318 // Skip the General Display Constants. 319 for (int i = 0; i < 4; ++i) 320 readLine(reader); 321 322 // Read in all the surface definitions and put them in the "surfaces" list. 323 readSurfaces(reader, tokenizer, unit, surfaces); 324 325 // Read in all the "curve" definitions (which are actually strings of points). 326 readCurves(reader, tokenizer, unit, surfaces, curves); 327 328 // Read in the patch defintitions into the "patches" list. 329 readPatches(reader, tokenizer, surfaces, curves, patches); 330 331 // Read in the source definitions into the "sources" list. 332 readSources(reader, tokenizer, sources, unit, surfaces); 333 334 } catch (NumberFormatException e) { 335 throw new IOException(MessageFormat.format(RESOURCES.getString("parseErrMsg"), 336 DESCRIPTION, reader.getLineNumber())); 337 } 338 } finally { 339 // Restore the default locale. 340 Locale.setDefault(defLocale); 341 } 342 343 return output; 344 } 345 346 /** 347 * Read in surface (and curve) definitions from the file./ 348 * 349 * @param reader The reader for reading lines of data from the file. 350 * @param tokenizer The tokenizer to use to tokenize each line. 351 * @param unit The unit used for interpreting the data. 352 * @param surfaces The list of surfaces to have all the new ones added to. 353 * @throws IOException If there is any problem reading from the file. 354 */ 355 private static void readSurfaces(LineNumberReader reader, TextTokenizer tokenizer, 356 Unit<Length> unit, GeomList surfaces) throws IllegalArgumentException, IOException { 357 358 // The next line should contain surface definitions. 359 String aLine = readLine(reader).trim(); 360 if (!"SURFACES".equals(aLine) && !"GTSURFACES".equals(aLine)) 361 throw new IOException(MessageFormat.format( 362 RESOURCES.getString("missingTypeData"), "SURFACES")); 363 364 // Read in the number of surfaces. 365 aLine = readLine(reader); 366 int nsurf = TypeFormat.parseInt(aLine); 367 368 // Skip 2 lines. 369 for (int i = 0; i < 2; ++i) 370 readLine(reader); 371 372 // Loop over all the surfaces. 373 for (int sidx = 0; sidx < nsurf; ++sidx) { 374 // Read in the code indicating if this is a trimmed surface. 375 aLine = readLine(reader); 376 tokenizer.setText(aLine); 377 tokenizer.nextToken(); 378 tokenizer.nextToken(); 379 Text token = tokenizer.nextToken(); 380 int itrim = TypeFormat.parseInt(token); 381 if (itrim == 1) { 382 System.out.println("itrim == 1, sidx = " + sidx); 383 System.out.println("Processing stopped"); 384 break; 385 } 386 387 // Read in the number of control points and degree of the surface. 388 aLine = readLine(reader); 389 tokenizer.setText(aLine); 390 token = tokenizer.nextToken(); 391 int k1 = TypeFormat.parseInt(token); 392 token = tokenizer.nextToken(); 393 int k2 = TypeFormat.parseInt(token); 394 tokenizer.nextToken(); 395 tokenizer.nextToken(); 396 token = tokenizer.nextToken(); 397 int m1 = TypeFormat.parseInt(token); 398 token = tokenizer.nextToken(); 399 int m2 = TypeFormat.parseInt(token); 400 401 // Read in the family name & IGES type. 402 aLine = readLine(reader); 403 tokenizer.setText(aLine); 404 tokenizer.nextToken(); 405 String family = tokenizer.nextToken().toString(); 406 String IGEStype = tokenizer.nextToken().toString(); 407 408 // Skip a line. 409 readLine(reader); 410 411 // Read in the S-direction knot vector. 412 int n = k1 + 1 + m1 + 1; // Number of knots. 413 double sStart = Double.MAX_VALUE; 414 double sEnd = -Double.MAX_VALUE; 415 double[] sKnots = new double[n]; 416 for (int i = 0; i < n; ++i) { 417 aLine = readLine(reader).trim(); 418 double u = TypeFormat.parseDouble(aLine); 419 sKnots[i] = u; 420 if (u < sStart) 421 sStart = u; 422 if (u > sEnd) 423 sEnd = u; 424 } 425 426 // Scale the knots into the range 0-1. 427 double[] scaledSKnots = sKnots; 428 if (Math.abs(sStart) > MathTools.EPS || Math.abs(sEnd - 1.0) > MathTools.EPS) { 429 scaledSKnots = new double[n]; 430 double m = 1. / (sEnd - sStart); 431 double b = -m * sStart; 432 for (int i = 0; i < n; ++i) { 433 double kv = sKnots[i]; 434 kv = scaleParam(m, b, kv); 435 scaledSKnots[i] = kv; 436 } 437 } 438 439 // Read in the T-direction knot vector. 440 n = k2 + 1 + m2 + 1; // Number of knots. 441 double[] tKnots = new double[n]; 442 double tStart = Double.MAX_VALUE; 443 double tEnd = -Double.MAX_VALUE; 444 for (int i = 0; i < n; ++i) { 445 aLine = readLine(reader).trim(); 446 double v = TypeFormat.parseDouble(aLine); 447 tKnots[i] = v; 448 if (v < tStart) 449 tStart = v; 450 if (v > tEnd) 451 tEnd = v; 452 } 453 454 // Scale the knots into the range 0-1. 455 double[] scaledTKnots = tKnots; 456 if (Math.abs(tStart) > MathTools.EPS || Math.abs(tEnd - 1.0) > MathTools.EPS) { 457 scaledTKnots = new double[n]; 458 double m = 1. / (tEnd - tStart); 459 double b = -m * tStart; 460 for (int i = 0; i < n; ++i) { 461 double kv = tKnots[i]; 462 kv = scaleParam(m, b, kv); 463 scaledTKnots[i] = kv; 464 } 465 } 466 467 // Create the knot vectors. 468 KnotVector kvS = KnotVector.newInstance(m1, scaledSKnots); 469 KnotVector kvT = KnotVector.newInstance(m2, scaledTKnots); 470 471 // Read in the control point coordinates. 472 double[][] cpsX = new double[k1 + 1][k2 + 1]; 473 double[][] cpsY = new double[k1 + 1][k2 + 1]; 474 double[][] cpsZ = new double[k1 + 1][k2 + 1]; 475 double[][] cpsW = new double[k1 + 1][k2 + 1]; 476 readArray(reader, tokenizer, k1, k2, cpsX); 477 readArray(reader, tokenizer, k1, k2, cpsY); 478 readArray(reader, tokenizer, k1, k2, cpsZ); 479 readArray(reader, tokenizer, k1, k2, cpsW); 480 481 // Skip 18 lines. 482 for (int i = 0; i < 18; ++i) 483 readLine(reader); 484 485 if (IGEStype.equals("-126")) { 486 // Collapsed surface. It's actually a curve. 487 FastTable<ControlPoint> cps = FastTable.newInstance(); 488 for (int i = 0; i <= k1; ++i) { 489 double weight = cpsW[i][0]; 490 double x = cpsX[i][0]; 491 double y = cpsY[i][0]; 492 double z = cpsZ[i][0]; 493 Point pnt = Point.valueOf(x, y, z, unit); 494 cps.add(ControlPoint.valueOf(pnt, weight)); 495 } 496 497 // Create the curve. 498 BasicNurbsCurve crv = BasicNurbsCurve.newInstance(cps, kvS); 499 crv.putUserData("GTC_U0", sStart); 500 crv.putUserData("GTC_U1", sEnd); 501 crv.putUserData("GTC_Family", family); 502 crv.putUserData("GTC_Type", IGEStype); 503 crv.setName(family); 504 surfaces.add(crv); 505 506 } else { 507 // A normal surface. 508 509 // Create the control point network. 510 ControlPoint[][] controlPoints = new ControlPoint[k2 + 1][k1 + 1]; 511 for (int i = 0; i <= k1; ++i) { 512 for (int j = 0; j <= k2; ++j) { 513 double weight = cpsW[i][j]; 514 double x = cpsX[i][j]; 515 double y = cpsY[i][j]; 516 double z = cpsZ[i][j]; 517 Point pnt = Point.valueOf(x, y, z, unit); 518 controlPoints[j][i] = ControlPoint.valueOf(pnt, weight); 519 } 520 } 521 ControlPointNet cpNet = ControlPointNet.valueOf(controlPoints); 522 523 // Create the surface. 524 BasicNurbsSurface surface = BasicNurbsSurface.newInstance(cpNet, kvS, kvT); 525 surface.putUserData("GTC_U0", sStart); 526 surface.putUserData("GTC_U1", sEnd); 527 surface.putUserData("GTC_V0", tStart); 528 surface.putUserData("GTC_V1", tEnd); 529 surface.putUserData("GTC_Family", family); 530 surface.putUserData("GTC_Type", IGEStype); 531 surface.setName(family); 532 surfaces.add(surface); 533 } 534 535 } 536 } // end readSurfaces() 537 538 /** 539 * Read a single array of data from the file. 540 */ 541 private static void readArray(LineNumberReader reader, TextTokenizer tokenizer, 542 int n1, int n2, double[][] arr) throws IOException, NumberFormatException { 543 String aLine = readLine(reader); 544 tokenizer.setText(aLine); 545 for (int i = 0; i <= n1; ++i) { 546 for (int j = 0; j <= n2; ++j) { 547 if (!tokenizer.hasMoreTokens()) { 548 aLine = readLine(reader); 549 tokenizer.setText(aLine); 550 } 551 Text token = tokenizer.nextToken(); 552 double v = TypeFormat.parseDouble(token); 553 arr[i][j] = v; 554 } 555 } 556 } 557 558 /** 559 * Scale the parameter value in the the required range from 0.0 to 1.0. 560 */ 561 private static double scaleParam(double m, double b, double value) { 562 // Scale the parameter value. 563 double kv = m * value + b; 564 565 // Watch for roundoff. 566 if (kv < 0.0) 567 kv = 0.0; 568 else if (kv > 1.0) 569 kv = 1.0; 570 571 return kv; 572 } 573 574 /** 575 * Read in the list of curves from the data file. "curves" are actually lists of 576 * points in GTC terminology. 577 * 578 * @param reader The reader for reading lines of data from the file. 579 * @param tokenizer The tokenizer to use to tokenize each line. 580 * @param unit The unit used for interpreting the data. 581 * @param surfaces The list of already existing surfaces (some curve points are 582 * subranges onto surfaces). 583 * @param curves The list to be filled in with PointString objects for each 584 * "curve". 585 * @throws IOException If there is any problem reading from the file. 586 */ 587 private static void readCurves(LineNumberReader reader, TextTokenizer tokenizer, Unit<Length> unit, 588 GeomList surfaces, GeomList<PointString> curves) throws NumberFormatException, IOException { 589 590 // The next line should contain "curve" definitions. 591 String aLine = readLine(reader).trim(); 592 if (!"CURVES".equals(aLine) && !"GTCURVES".equals(aLine)) 593 throw new IOException(MessageFormat.format( 594 RESOURCES.getString("missingTypeData"), "CURVES")); 595 596 // Read in the number of curves. 597 aLine = readLine(reader); 598 int ncrvs = TypeFormat.parseInt(aLine); 599 600 // Skip 2 lines. 601 for (int i = 0; i < 2; ++i) 602 readLine(reader); 603 604 // Loop over all the "curves" (which are actually stings of points). 605 for (int cidx = 0; cidx < ncrvs; ++cidx) { 606 // Read in the number of points in this curve. 607 aLine = readLine(reader); 608 tokenizer.setText(aLine); 609 tokenizer.nextToken(); 610 tokenizer.nextToken(); 611 Text token = tokenizer.nextToken(); 612 int npts = TypeFormat.parseInt(token); 613 614 // Get the name of this "curve". 615 tokenizer.nextToken(); 616 tokenizer.nextToken(); 617 String crvName = tokenizer.nextToken().toString(); 618 619 // Skip a line. 620 readLine(reader); 621 622 // Read in all the points for this curve. 623 PointString str = PointString.newInstance(); 624 str.setName(crvName); 625 for (int i = 0; i < npts; ++i) { 626 aLine = readLine(reader); 627 tokenizer.setText(aLine); 628 token = tokenizer.nextToken(); 629 double x = TypeFormat.parseDouble(token); 630 token = tokenizer.nextToken(); 631 double y = TypeFormat.parseDouble(token); 632 token = tokenizer.nextToken(); 633 double z = TypeFormat.parseDouble(token); 634 token = tokenizer.nextToken(); 635 double u = TypeFormat.parseDouble(token); 636 token = tokenizer.nextToken(); 637 double v = TypeFormat.parseDouble(token); 638 token = tokenizer.nextToken(); 639 int j = TypeFormat.parseInt(token); 640 641 if (j == 0) { 642 // Not attached to a "surface". 643 Point p = Point.valueOf(x, y, z, unit); 644 str.add(p); 645 646 } else { 647 SubrangePoint p; 648 --j; // Convert index from unit offset to 0 offset. 649 if (surfaces.get(j) instanceof BasicNurbsSurface) { 650 BasicNurbsSurface Ssrf = (BasicNurbsSurface)surfaces.get(j); 651 652 // Extract the original parametric bounds of the surface. 653 double sStart = (Double)Ssrf.getUserData("GTC_U0"); 654 double tStart = (Double)Ssrf.getUserData("GTC_V0"); 655 double sEnd = (Double)Ssrf.getUserData("GTC_U1"); 656 double tEnd = (Double)Ssrf.getUserData("GTC_V1"); 657 658 // Scale the parameters into range. 659 double mS = 1. / (sEnd - sStart); 660 double bS = -mS * sStart; 661 double mT = 1. / (tEnd - tStart); 662 double bT = -mT * tStart; 663 u = scaleParam(mS, bS, u); 664 v = scaleParam(mT, bT, v); 665 666 // Create the point. 667 p = Ssrf.getPoint(u, v); 668 669 } else { 670 BasicNurbsCurve Bcrv = (BasicNurbsCurve)surfaces.get(j); 671 672 // Extract the original parametric bounds of the surface. 673 double sStart = (Double)Bcrv.getUserData("GTC_U0"); 674 double sEnd = (Double)Bcrv.getUserData("GTC_U1"); 675 676 // Scale the parameter into range. 677 double mS = 1. / (sEnd - sStart); 678 double bS = -mS * sStart; 679 u = scaleParam(mS, bS, u); 680 681 // Create the point. 682 p = Bcrv.getPoint(u); 683 } 684 str.add(p); 685 } 686 } 687 curves.add(str); 688 } 689 690 } // end readCurves() 691 692 /** 693 * Read in the patch definitions from the file. 694 * 695 * @param reader The reader for reading lines of data from the file. 696 * @param tokenizer The tokenizer to use to tokenize each line. 697 * @param unit The unit used for interpreting the data. 698 * @param surfaces The list of already existing surfaces (some patches lie on top of 699 * surfaces). 700 * @param curves The list of already existing curve definitions (patch boundaries 701 * are made up of curves). 702 * @param patches The list of patches to be filled in by this method. 703 * @throws IOException If there is any problem reading from the file. 704 */ 705 private static void readPatches(LineNumberReader reader, TextTokenizer tokenizer, GeomList surfaces, 706 GeomList<PointString> curves, GeomList patches) throws IOException { 707 708 // The next line should contain "patch" definitions. 709 String aLine = readLine(reader).trim(); 710 if (!"PATCHES".equals(aLine) && !"GTPATCHES".equals(aLine)) 711 throw new IOException(MessageFormat.format( 712 RESOURCES.getString("missingTypeData"), "PATCHES")); 713 714 // Read in the number of patches. 715 aLine = readLine(reader); 716 int npatches = TypeFormat.parseInt(aLine); 717 718 // Skip 3 lines. 719 for (int i = 0; i < 3; ++i) 720 readLine(reader); 721 722 // Loop over all the patches. 723 for (int pidx = 0; pidx < npatches; ++pidx) { 724 GeomList patch = GeomList.newInstance(); 725 726 aLine = readLine(reader); 727 tokenizer.setText(aLine); 728 tokenizer.nextToken(); 729 tokenizer.nextToken(); 730 tokenizer.nextToken(); 731 tokenizer.nextToken(); 732 733 // Read in the number of edges for the patch. 734 Text token = tokenizer.nextToken(); 735 int nedges = TypeFormat.parseInt(token); 736 737 // Read in the patch type code. 738 token = tokenizer.nextToken(); 739 int pType = TypeFormat.parseInt(token); 740 patch.putUserData("GTC_PatchType", pType); 741 742 // Read in the boundary condition type code. 743 token = tokenizer.nextToken(); 744 int BCType = TypeFormat.parseInt(token); 745 patch.putUserData("GTC_BCType", BCType); 746 747 // Read in the family name. 748 String family = tokenizer.nextToken().toString(); 749 patch.putUserData("GTC_Family", family); 750 751 // Read in the next line. 752 aLine = readLine(reader); 753 tokenizer.setText(aLine); 754 755 // Read in the d3m number 756 token = tokenizer.nextToken(); 757 int d3m = TypeFormat.parseInt(token); 758 patch.putUserData("GTC_D3MNumber", d3m); 759 patch.putUserData("GTC_ValidPatch", (d3m == -1 ? Boolean.FALSE : Boolean.TRUE)); 760 761 // Read in the number of surfaces (should be either 0 or 1). 762 token = tokenizer.nextToken(); 763 int nsrf = TypeFormat.parseInt(token); 764 if (nsrf < 0 || nsrf > 1) 765 throw new IOException(MessageFormat.format( 766 RESOURCES.getString("patchSrfNumErr"), (pidx + 1), nsrf)); 767 768 GeomList srfLst = GeomList.newInstance(); 769 patch.add(srfLst); 770 if (nsrf > 0) { 771 // Read in the surface related information. 772 aLine = readLine(reader); 773 tokenizer.setText(aLine); 774 775 // Read in the surface ID number 776 token = tokenizer.nextToken(); 777 int srfID = TypeFormat.parseInt(token); 778 srfLst.add(surfaces.get(srfID - 1)); 779 780 // Skip the next two lines. 781 readLine(reader); 782 readLine(reader); 783 } 784 785 // Create a list of edges. 786 GeomList<PointString> edgeLst = GeomList.newInstance(); 787 patch.add(edgeLst); 788 789 // Create a list of side index numbers. 790 FastTable<Integer> sides = FastTable.newInstance(); 791 patch.putUserData("GTC_EdgeSideIndexes", sides); 792 793 // Create a list of flags indicating if the edge curve is reversed or not. 794 FastTable<Boolean> reversed = FastTable.newInstance(); 795 patch.putUserData("GTC_EdgeReversedFlags", reversed); 796 797 // Loop over the edgeLst. 798 int side = 0; 799 for (int eidx = 0; eidx < nedges; ++eidx) { 800 // Read in the edge. 801 aLine = readLine(reader); 802 tokenizer.setText(aLine); 803 804 // Skip the edge ID number (sequential). 805 tokenizer.nextToken(); 806 807 // Is the edge reversed? 808 token = tokenizer.nextToken(); 809 int rev = TypeFormat.parseInt(token); 810 if (rev == -1) 811 reversed.add(Boolean.TRUE); 812 else 813 reversed.add(Boolean.FALSE); 814 815 // Read in the curve ID number. 816 token = tokenizer.nextToken(); 817 int crvID = TypeFormat.parseInt(token); 818 edgeLst.add(curves.get(crvID - 1)); 819 820 // Read in the side flag. 821 token = tokenizer.nextToken(); 822 int sideFlg = TypeFormat.parseInt(token); 823 if (sideFlg == 1 || eidx == 0) 824 ++side; 825 sides.add(side); 826 } 827 828 // Save this patch in the output list. 829 patches.add(patch); 830 831 } // Next pidx 832 833 } // end readPatches() 834 835 /** 836 * Read in the list of sources (or "background grid") from the file. 837 * 838 * @param reader The reader for reading lines of data from the file. 839 * @param tokenizer The tokenizer to use to tokenize each line. 840 * @param unit The unit used for interpreting the data. 841 * @param surfaces The list of already existing surfaces (some curve source are 842 * subranges onto surfaces). 843 * @param sources The list to be filled in with information for each source. 844 * @throws IOException If there is a problem reading from the file. 845 */ 846 private static void readSources(LineNumberReader reader, TextTokenizer tokenizer, GeomList sources, 847 Unit<Length> unit, GeomList surfaces) throws IOException, NumberFormatException { 848 849 // The next line should contain "source" definitions. 850 String aLine = readLine(reader).trim(); 851 if (!"Background Grid".equals(aLine)) 852 throw new IOException(MessageFormat.format( 853 RESOURCES.getString("missingTypeData"), "Background Grid")); 854 855 // Read in the number of sources. 856 aLine = readLine(reader); 857 int nsources = TypeFormat.parseInt(aLine); 858 859 // Read in the global parameters. 860 aLine = readLine(reader); 861 tokenizer.setText(aLine); 862 863 // Read in the delta1 parameter. 864 Text token = tokenizer.nextToken(); 865 double delta1 = TypeFormat.parseDouble(token); 866 sources.putUserData("GTC_Delta1", delta1); 867 868 // Read in the rate1 parameter. 869 token = tokenizer.nextToken(); 870 double rate1 = TypeFormat.parseDouble(token); 871 sources.putUserData("GTC_Rate1", rate1); 872 873 // Read in the rate2 parameter. 874 token = tokenizer.nextToken(); 875 double rate2 = TypeFormat.parseDouble(token); 876 sources.putUserData("GTC_Rate2", rate2); 877 878 // Read in the number of layers parameter. 879 token = tokenizer.nextToken(); 880 int nLayer = TypeFormat.parseInt(token); 881 sources.putUserData("GTC_NLayers", nLayer); 882 883 aLine = readLine(reader); 884 tokenizer.setText(aLine); 885 886 // Read in the ni parameter. 887 token = tokenizer.nextToken(); 888 int ni = TypeFormat.parseInt(token); 889 sources.putUserData("GTC_NIterations", ni); 890 891 // Skip one. 892 tokenizer.nextToken(); 893 894 // Read in the stretching flag. 895 token = tokenizer.nextToken(); 896 int stretching = TypeFormat.parseInt(token); 897 sources.putUserData("GTC_Stretching", (stretching == 1 ? Boolean.TRUE : Boolean.FALSE)); 898 899 // Read in the grow viscous layers flag. 900 token = tokenizer.nextToken(); 901 int viscous = TypeFormat.parseInt(token); 902 sources.putUserData("GTC_ViscousLayers", (viscous == 1 ? Boolean.TRUE : Boolean.FALSE)); 903 904 // Loop over all the sources. 905 for (int sidx = 0; sidx < nsources; ++sidx) { 906 // A "source" consists of two points. The 2nd one is sometimes unnecessary, but always present. 907 PointString<GeomPoint> source = PointString.newInstance(); 908 sources.add(source); 909 910 aLine = readLine(reader); 911 tokenizer.setText(aLine); 912 tokenizer.nextToken(); 913 tokenizer.nextToken(); 914 tokenizer.nextToken(); 915 tokenizer.nextToken(); 916 917 // Read in the source type. 918 token = tokenizer.nextToken(); 919 int type = TypeFormat.parseInt(token); 920 source.putUserData("GTC_SourceType", type); 921 922 // Read in the source family. 923 String family = tokenizer.nextToken().toString(); 924 source.putUserData("GTC_Family", family); 925 926 // Read in the stretching direction. 927 aLine = readLine(reader); 928 tokenizer.setText(aLine); 929 token = tokenizer.nextToken(); 930 int stetchingDir = TypeFormat.parseInt(token); 931 source.putUserData("GTC_StretchingDir", stetchingDir); 932 933 // Read in an. 934 aLine = readLine(reader); 935 tokenizer.setText(aLine); 936 token = tokenizer.nextToken(); 937 double an = TypeFormat.parseDouble(token); 938 source.putUserData("GTC_an", an); 939 940 // Read in bn. 941 token = tokenizer.nextToken(); 942 double bn = TypeFormat.parseDouble(token); 943 source.putUserData("GTC_bn", bn); 944 945 // Read in alpha. 946 token = tokenizer.nextToken(); 947 double alpha = TypeFormat.parseDouble(token); 948 source.putUserData("GTC_Alpha", alpha); 949 950 // Read in the two points. 951 for (int i = 0; i < 2; ++i) { 952 // Read in s. 953 aLine = readLine(reader); 954 tokenizer.setText(aLine); 955 token = tokenizer.nextToken(); 956 double smalls = TypeFormat.parseDouble(token); 957 958 // Read in S. 959 token = tokenizer.nextToken(); 960 double capS = TypeFormat.parseDouble(token); 961 962 // Read in ro. 963 token = tokenizer.nextToken(); 964 double ro = TypeFormat.parseDouble(token); 965 966 // Read in ri. 967 token = tokenizer.nextToken(); 968 double ri = TypeFormat.parseDouble(token); 969 970 // Skip next line. 971 readLine(reader); 972 973 // Read in delta1. 974 aLine = readLine(reader); 975 tokenizer.setText(aLine); 976 token = tokenizer.nextToken(); 977 delta1 = TypeFormat.parseDouble(token); 978 979 // Read in rate1. 980 token = tokenizer.nextToken(); 981 rate1 = TypeFormat.parseDouble(token); 982 983 // Read in rate2. 984 token = tokenizer.nextToken(); 985 rate2 = TypeFormat.parseDouble(token); 986 987 // Read in the point coordinates. 988 aLine = readLine(reader); 989 tokenizer.setText(aLine); 990 token = tokenizer.nextToken(); 991 double x = TypeFormat.parseDouble(token); 992 token = tokenizer.nextToken(); 993 double y = TypeFormat.parseDouble(token); 994 token = tokenizer.nextToken(); 995 double z = TypeFormat.parseDouble(token); 996 token = tokenizer.nextToken(); 997 double u = TypeFormat.parseDouble(token); 998 token = tokenizer.nextToken(); 999 double v = TypeFormat.parseDouble(token); 1000 token = tokenizer.nextToken(); 1001 int j = TypeFormat.parseInt(token); 1002 1003 if (j == 0) { 1004 // Not attached to a "surface". 1005 Point p = Point.valueOf(x, y, z, unit); 1006 source.add(p); 1007 1008 } else { 1009 SubrangePoint p; 1010 --j; // Convert index from unit offset to 0 offset. 1011 if (surfaces.get(j) instanceof BasicNurbsSurface) { 1012 BasicNurbsSurface Ssrf = (BasicNurbsSurface)surfaces.get(j); 1013 1014 // Extract the original parametric bounds of the surface. 1015 double sStart = (Double)Ssrf.getUserData("GTC_U0"); 1016 double tStart = (Double)Ssrf.getUserData("GTC_V0"); 1017 double sEnd = (Double)Ssrf.getUserData("GTC_U1"); 1018 double tEnd = (Double)Ssrf.getUserData("GTC_V1"); 1019 1020 // Scale the parameters into range. 1021 double mS = 1. / (sEnd - sStart); 1022 double bS = -mS * sStart; 1023 double mT = 1. / (tEnd - tStart); 1024 double bT = -mT * tStart; 1025 u = scaleParam(mS, bS, u); 1026 v = scaleParam(mT, bT, v); 1027 1028 // Create the point. 1029 p = Ssrf.getPoint(u, v); 1030 1031 } else { 1032 BasicNurbsCurve Bcrv = (BasicNurbsCurve)surfaces.get(j); 1033 1034 // Extract the original parametric bounds of the surface. 1035 double sStart = (Double)Bcrv.getUserData("GTC_U0"); 1036 double sEnd = (Double)Bcrv.getUserData("GTC_U1"); 1037 1038 // Scale the parameter into range. 1039 double mS = 1. / (sEnd - sStart); 1040 double bS = -mS * sStart; 1041 u = scaleParam(mS, bS, u); 1042 1043 // Create the point. 1044 p = Bcrv.getPoint(u); 1045 } 1046 source.add(p); 1047 } 1048 1049 // Store the meta-data for this point. 1050 GeomPoint p = source.get(i); 1051 p.putUserData("GTC_s", smalls); 1052 p.putUserData("GTC_S", capS); 1053 p.putUserData("GTC_ro", ro); 1054 p.putUserData("GTC_ri", ri); 1055 p.putUserData("GTC_Delta1", delta1); 1056 p.putUserData("GTC_Rate1", rate1); 1057 p.putUserData("GTC_Rate2", rate2); 1058 1059 } // Next i 1060 1061 // Skip the next 8*4 lines. 1062 for (int i = 0; i < 8 * 4; ++i) { 1063 readLine(reader); 1064 } 1065 1066 } // Next sidx 1067 1068 } // end readSources() 1069 1070 /** 1071 * Returns <code>true</code>. This reader can write some entity types to a GTC file. 1072 * 1073 * @return true 1074 */ 1075 @Override 1076 public boolean canWriteData() { 1077 return true; 1078 } 1079 1080 /** 1081 * Writes out a geometry file for the geometry contained in the supplied 1082 * {@link GeometryList} object. If the input geometry is not 3D, it will be forced to 1083 * be 3D (by padding if there are too few or by truncating additional dimensions). 1084 * <p> 1085 * WARNING: This format is not unit aware. The geometry will be written out in 1086 * whatever its current units are! Make sure to convert to the desired units for the 1087 * file before calling this method. 1088 * </p> 1089 * 1090 * @param outputFile The output File to which the geometry is to be written. May not 1091 * be null. 1092 * @param geometry The {@link GeometryList} object containing the geometry to be 1093 * written out. May not be null. 1094 * @throws IOException If there is a problem writing to the specified file. 1095 */ 1096 @Override 1097 public void write(File outputFile, GeometryList geometry) throws IOException { 1098 requireNonNull(outputFile); 1099 1100 _warnings.clear(); 1101 if (!geometry.containsGeometry()) { 1102 _warnings.add(RESOURCES.getString("noGeometryWarning")); 1103 return; 1104 } 1105 1106 // Check for input data formatting. 1107 int size = geometry.size(); 1108 if (size > 4) 1109 throw new IOException(MessageFormat.format(RESOURCES.getString("inputGeomErr1"), size)); 1110 for (int i = 0; i < size; ++i) { 1111 Object elem = geometry.get(i); 1112 if (!(elem instanceof GeomList)) 1113 throw new IOException(MessageFormat.format( 1114 RESOURCES.getString("inputGeomErr2"), elem.getClass(), i + 1)); 1115 } 1116 1117 // Pull out the big pieces. 1118 GeomList surfaces = (GeomList)geometry.get(0); 1119 GeomList<PointString<GeomPoint>> curves; 1120 if (size > 1) 1121 curves = (GeomList<PointString<GeomPoint>>)geometry.get(1); 1122 else 1123 curves = GeomList.newInstance(); 1124 GeomList<GeomList> patches; 1125 if (size > 2) 1126 patches = (GeomList<GeomList>)geometry.get(2); 1127 else 1128 patches = GeomList.newInstance(); 1129 GeomList<PointString<GeomPoint>> sources; 1130 if (size == 4) 1131 sources = (GeomList<PointString<GeomPoint>>)geometry.get(3); 1132 else 1133 sources = GeomList.newInstance(); 1134 1135 // The surfaces list must contain only NURBS surfaces and curves. 1136 size = surfaces.size(); 1137 for (int i = 0; i < size; ++i) { 1138 Object elem = surfaces.get(i); 1139 if (!(elem instanceof NurbsSurface) && !(elem instanceof NurbsCurve)) 1140 throw new IOException(MessageFormat.format(RESOURCES.getString("elementTypeErr"), 1141 "SURFACES", "NURBS surfaces & curves", elem.getClass(), i + 1)); 1142 } 1143 1144 // The curves list must contain only PointString objects. 1145 size = curves.size(); 1146 for (int i = 0; i < size; ++i) { 1147 Object elem = curves.get(i); 1148 if (!(elem instanceof PointString)) 1149 throw new IOException(MessageFormat.format(RESOURCES.getString("elementTypeErr"), 1150 "CURVES", "PointString objects", elem.getClass(), i + 1)); 1151 } 1152 1153 // The patches list must contain lists that contain exactly two geometry lists; 1154 // 1st one with at most 1 surface, 2nd with a list of PointStrings. 1155 size = patches.size(); 1156 for (int i = 0; i < size; ++i) { 1157 Object elem = patches.get(i); 1158 if (!(elem instanceof GeomList)) 1159 throw new IOException(MessageFormat.format(RESOURCES.getString("elementTypeErr"), 1160 "PATCHES", "GeomList objects", elem.getClass(), i + 1)); 1161 1162 GeomList<GeomList> patch = (GeomList<GeomList>)elem; 1163 if (patch.size() != 2) 1164 throw new IOException(MessageFormat.format( 1165 RESOURCES.getString("patchInputErr1"), patch.size(), i + 1)); 1166 1167 for (int j = 0; j < 2; ++j) { 1168 elem = patch.get(j); 1169 if (!(elem instanceof GeomList)) 1170 throw new IOException(MessageFormat.format( 1171 RESOURCES.getString("patchInputErr2"), elem.getClass(), j + 1, i + 1)); 1172 } 1173 1174 if (patch.get(0).size() > 1) 1175 throw new IOException(MessageFormat.format( 1176 RESOURCES.getString("patchMultSrfErr"), patch.get(0).size(), i + 1)); 1177 GeomList srfLst = patch.get(0); 1178 if (srfLst.size() > 0 && !(srfLst.get(0) instanceof NurbsSurface)) 1179 throw new IOException(MessageFormat.format( 1180 RESOURCES.getString("patchSubListTypeErr"), srfLst.get(0).getClass())); 1181 GeomList edges = patch.get(1); 1182 if (edges.size() < 3) 1183 throw new IOException(MessageFormat.format( 1184 RESOURCES.getString("patchEdgeCountErr"), edges.size(), i + 1)); 1185 for (int j = 0; j < edges.size(); ++j) { 1186 elem = edges.get(j); 1187 if (!(elem instanceof PointString)) 1188 throw new IOException(MessageFormat.format( 1189 RESOURCES.getString("patchEdgeTypeErr"), elem.getClass(), j + 1, i + 1)); 1190 } 1191 } 1192 1193 // The sources list must contain only PointString objects each of which contains exactly 2 points. 1194 size = sources.size(); 1195 for (int i = 0; i < size; ++i) { 1196 Object elem = sources.get(i); 1197 if (!(elem instanceof PointString)) 1198 throw new IOException(MessageFormat.format(RESOURCES.getString("elementTypeErr"), 1199 "SOURCES", "PointString objects", elem.getClass(), i + 1)); 1200 PointString source = sources.get(i); 1201 if (source.size() != 2) 1202 throw new IOException(MessageFormat.format( 1203 RESOURCES.getString("sourceSizeErr"), source.size(), i + 1)); 1204 } 1205 1206 // GTC files are required to be in ASCII with U.S. style number formatting. 1207 // Get the default locale and save it. Then change the default locale to US. 1208 Locale defLocale = Locale.getDefault(); 1209 Locale.setDefault(Locale.US); 1210 1211 // Create a PrintWriter that writes to the specified file. 1212 StackContext.enter(); 1213 try (PrintWriter writer = new PrintWriter(outputFile, StandardCharsets.US_ASCII.name())) { 1214 1215 // Write out the header information. 1216 Unit<Length> units = geometry.getUnit(); 1217 setFileUnits(units); 1218 writeHeader(writer, outputFile.getAbsolutePath(), geometry); 1219 1220 // Write out the NURBS surfaces & curves. 1221 writeSurfaces(writer, surfaces, units); 1222 1223 // Write out the CURVE data. 1224 writeCurves(writer, curves, units, surfaces); 1225 1226 // Write out the PATCHES data. 1227 writePatches(writer, patches, surfaces, curves); 1228 1229 // Write out the SOURCES data. 1230 writer.println("Background Grid"); 1231 int nSources = sources.size(); 1232 writer.println(0); 1233 writer.println("0.005 0.15 0.02 100"); 1234 writer.println("20 0 0 0"); 1235 1236 // Write out some mystery numbers. 1237 writer.println("1"); 1238 writer.println("0"); 1239 writer.println("0"); 1240 writer.println("0"); 1241 writer.println("1"); 1242 writer.println("0"); 1243 writer.println("0"); 1244 writer.println("0"); 1245 writer.println("1"); 1246 writer.println("1 1 20"); 1247 writer.println("1 -1 0 0"); 1248 1249 // Write out the default SPAR parameters. 1250 writer.println("SPAR"); 1251 writer.println("0"); 1252 writer.println("15"); 1253 writer.println("0.1"); 1254 writer.println("0.1"); 1255 writer.println("0.1"); 1256 writer.println("0"); 1257 writer.println("0"); 1258 1259 } finally { 1260 StackContext.exit(); 1261 1262 // Restore the default locale. 1263 Locale.setDefault(defLocale); 1264 } 1265 1266 } 1267 1268 /** 1269 * Write out the header for the file. 1270 * 1271 * @param writer The PrintWriter we are writing to the file with. 1272 * @param fileName The path to the file being written out. 1273 * @param geometry The geometry being written out. 1274 */ 1275 private void writeHeader(PrintWriter writer, String fileName, GeometryList geometry) { 1276 // Write out the date & time this file was written. 1277 Date theDate = new Date(); 1278 SimpleDateFormat fmt = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", Locale.US); 1279 writer.print("The restart file was written on "); 1280 writer.println(fmt.format(theDate)); 1281 1282 // Write out the file name. 1283 writer.print("File name is "); 1284 writer.println(fileName); 1285 1286 // Write out GTC information. 1287 writer.println("GTC 2010 - Build 20091209"); 1288 1289 // Write out general constants. 1290 writer.println("General Constants"); 1291 1292 // Write out the version. 1293 String ver = (String)geometry.getUserData("GTC_Version"); 1294 if (isNull(ver)) 1295 ver = "4.1"; 1296 writer.print("Version "); writer.print(ver); 1297 1298 // Write out the global tolerance value. 1299 Parameter<Length> tol = (Parameter<Length>)geometry.getUserData("GTC_Tol"); 1300 if (isNull(tol)) 1301 tol = Parameter.valueOf(1e-6, getFileUnits()); 1302 writer.print(" "); writer.print(tol.getValue()); 1303 writer.println(" -1"); 1304 1305 // Write out the model name. 1306 String name = (String)geometry.getUserData("GTC_Name"); 1307 if (isNull(name)) 1308 name = geometry.getName(); 1309 if (isNull(name)) 1310 name = "MyProject"; 1311 writer.println(name); 1312 1313 // Write out general display constants. 1314 writer.println("General Display Constants"); 1315 writer.println("0 0"); 1316 writer.println("99 99 0 0"); 1317 writer.println("99 99 0 0"); 1318 1319 } 1320 1321 /** 1322 * Write out the list of surfaces to a GTC restart file. 1323 * 1324 * @param writer The PrintWriter we are writing to the file with. 1325 * @param surfaces The list of surfaces to be written out. 1326 * @param units The units for the geometry in the file. 1327 */ 1328 private static void writeSurfaces(PrintWriter writer, GeomList surfaces, Unit<Length> units) { 1329 1330 // Write out the SURFACE data. 1331 writer.println("SURFACES"); 1332 int nSrfs = surfaces.size(); 1333 writer.println(nSrfs); 1334 writer.println("99 99 99 99"); 1335 writer.println("99 99 99 99"); 1336 1337 // Loop over all the NURBS surfaces & curves. 1338 Parameter<Length> srfTol = Parameter.valueOf(1e-4, units); 1339 for (int sidx = 0; sidx < nSrfs; ++sidx) { 1340 Object elem = surfaces.get(sidx); 1341 1342 boolean isCrv = false; 1343 NurbsSurface srf; 1344 if (elem instanceof NurbsCurve) { 1345 // Convert curves to collapsed surfaces. 1346 NurbsCurve crv = ((NurbsCurve)elem).to(units); 1347 GeomList<NurbsCurve> crvs = GeomList.newInstance(); 1348 crvs.add(crv); 1349 crvs.add(crv); 1350 srf = SurfaceFactory.createSkinnedSurface(crvs, 1, srfTol); 1351 srf.putAllUserData(crv.getAllUserData()); 1352 srf.setName(crv.getName()); 1353 isCrv = true; 1354 } else 1355 srf = ((NurbsSurface)elem).to(units); 1356 1357 // Write out the surface number line. 1358 writer.print("Surface_Number "); 1359 writer.print(sidx + 1); 1360 writer.println(" 0"); 1361 1362 // Write out the number of control points and degree of the surface in each direction. 1363 int k1 = srf.getNumberOfRows(); 1364 int k2 = srf.getNumberOfColumns(); 1365 int m1 = srf.getSDegree(); 1366 int m2 = srf.getTDegree(); 1367 int n1 = k1 + m1; 1368 int n2 = k2 + m2; 1369 writer.print(k1 - 1); writer.print(" "); 1370 writer.print(k2 - 1); writer.print(" "); 1371 writer.print(n1); writer.print(" "); 1372 writer.print(n2); writer.print(" "); 1373 writer.print(m1); writer.print(" "); 1374 writer.println(m2); 1375 1376 // Write out a mystery number. 1377 writer.print("1 "); 1378 1379 // Write out the family name. 1380 String family = (String)srf.getUserData("GTC_Family"); 1381 if (isNull(family)) { 1382 family = srf.getName(); 1383 if (isNull(family)) 1384 family = (isCrv ? "L0E126" : "L0E128"); 1385 } 1386 writer.print(family); writer.print(" "); 1387 1388 // Write out the surface IGES type code. 1389 Object IGESType = srf.getUserData("GTC_Type"); 1390 if (isNull(IGESType)) 1391 IGESType = (isCrv ? -126 : 128); 1392 writer.print(IGESType); 1393 writer.println(" 99"); 1394 1395 // Write a line of mystery numbers. 1396 writer.println("0 0 0 0"); 1397 1398 // Write out the knot vector in the S direction. 1399 KnotVector kS = srf.getSKnotVector(); 1400 int nKnots = kS.length(); 1401 for (int k = 0; k < nKnots; ++k) { 1402 double value = kS.getValue(k); 1403 writer.printf("%15.9g", value); 1404 writer.println(); 1405 } 1406 1407 // Write out the knot vector in the T direction. 1408 KnotVector kT = srf.getTKnotVector(); 1409 nKnots = kT.length(); 1410 for (int k = 0; k < nKnots; ++k) { 1411 double value = kT.getValue(k); 1412 writer.printf("%15.9g", value); 1413 writer.println(); 1414 } 1415 1416 // Write out the control points. 1417 ControlPointNet cpNet = srf.getControlPoints(); 1418 1419 // Write out the X-coordinates. 1420 int count = 0; 1421 for (int i = 0; i < k1; ++i) { 1422 for (int j = 0; j < k2; ++j) { 1423 ControlPoint cp = cpNet.get(i, j); 1424 writer.printf("%15.9g ", cp.getPoint().getValue(0)); 1425 ++count; 1426 if (count > 4) { 1427 count = 0; 1428 writer.println(); 1429 } 1430 } 1431 } 1432 if (count != 0) 1433 writer.println(); 1434 1435 // Write out the Y-coordinates. 1436 count = 0; 1437 for (int i = 0; i < k1; ++i) { 1438 for (int j = 0; j < k2; ++j) { 1439 ControlPoint cp = cpNet.get(i, j); 1440 writer.printf("%15.9g ", cp.getPoint().getValue(1)); 1441 ++count; 1442 if (count > 4) { 1443 count = 0; 1444 writer.println(); 1445 } 1446 } 1447 } 1448 if (count != 0) 1449 writer.println(); 1450 1451 // Write out the Z-coordinates. 1452 count = 0; 1453 for (int i = 0; i < k1; ++i) { 1454 for (int j = 0; j < k2; ++j) { 1455 ControlPoint cp = cpNet.get(i, j); 1456 writer.printf("%15.9g ", cp.getPoint().getValue(2)); 1457 ++count; 1458 if (count > 4) { 1459 count = 0; 1460 writer.println(); 1461 } 1462 } 1463 } 1464 if (count != 0) 1465 writer.println(); 1466 1467 // Write out k1*k2 "1" values (I don't know what they are for). 1468 int size = k1 * k2; 1469 count = 0; 1470 for (int i = 0; i < size; ++i) { 1471 writer.print(" 1 "); 1472 ++count; 1473 if (count > 4) { 1474 count = 0; 1475 writer.println(); 1476 } 1477 } 1478 if (count != 0) 1479 writer.println(); 1480 1481 // Write out 8 mystery number lines. 1482 writer.println(" 0 1 0 1"); 1483 writer.println(" 0 1 0 1"); 1484 writer.println(" 0.0001 0.0001 0.5 0.5"); 1485 writer.println(" 0.1 1e-05 0 0"); 1486 writer.println("0 1 0 1"); 1487 writer.println("0 0 0 0"); 1488 writer.println("0 0 0 0"); 1489 writer.println("0 0 0 0"); 1490 1491 // Write out the default display path. 1492 writer.print("0 "); 1493 if (isCrv) { 1494 if (k1 < 2) 1495 k1 = 3; 1496 k2 = 2; 1497 } else { 1498 if (k1 < 2) 1499 k1 = 3; 1500 if (k2 < 2) 1501 k2 = 3; 1502 } 1503 writer.print(k1); writer.print(" 0 "); 1504 writer.println(k2); 1505 1506 // Write out the current display path. 1507 writer.print("0 "); 1508 writer.print(k1); writer.print(" 1 "); 1509 writer.println(k1); 1510 writer.print("0 "); 1511 writer.print(k2); writer.print(" 1 "); 1512 writer.println(k2); 1513 1514 // Write some mystery numbers. 1515 writer.println("0 0 0 0"); 1516 writer.println("1 2 1000 0"); 1517 1518 // Write out display code and color (0 = don't draw, 1 = boundary, 2 = wireframe). 1519 // Don't understand how colors are encoded, but 4287365120 == 0,0,140 in RGB. 1520 writer.println("2 4287365120 99 99"); 1521 for (int i = 0; i < 4; ++i) { 1522 writer.println("99 99 99 99"); 1523 } 1524 1525 } // Next sidx 1526 1527 } // end writeSurfaces() 1528 1529 /** 1530 * 1531 * @param writer The PrintWriter we are writing to the file with. 1532 * @param curves The list of curve entities to write out (actually a list of 1533 * PointStrings). 1534 * @param units The units for the geometry in the file. 1535 * @param surfaces The list of surfaces that the points in the curves may be subranged 1536 * onto. 1537 * @throws IOException If there is any problem writing to the file. 1538 */ 1539 private static void writeCurves(PrintWriter writer, GeomList<PointString<GeomPoint>> curves, 1540 Unit<Length> units, GeomList surfaces) throws IOException { 1541 1542 // Write out the CURVE data. 1543 writer.println("CURVES"); 1544 int nCrvs = curves.size(); 1545 writer.println(nCrvs); 1546 writer.println("99 99 99 99"); 1547 writer.println("99 99 99 99"); 1548 1549 // Loop over all the curves. 1550 for (int cidx = 0; cidx < nCrvs; ++cidx) { 1551 PointString<GeomPoint> crv = curves.get(cidx).to(units); 1552 int npts = crv.size(); 1553 1554 // Write out the curve index 1555 writer.print("Curve_Number "); 1556 writer.print(cidx + 1); writer.print(" "); 1557 1558 // Write out the number of points in the curve. 1559 writer.print(npts); writer.print(" "); 1560 1561 // Write out a pair of mystery numbers. 1562 writer.print("0 2271780 "); 1563 1564 // Write out the family name for the curve. 1565 String family = (String)crv.getUserData("GTC_Family"); 1566 if (isNull(family)) { 1567 family = crv.getName(); 1568 if (isNull(family)) 1569 family = "Addams"; 1570 } 1571 writer.println(family); 1572 1573 // Write some mystery numbers. 1574 writer.println("99 99 99 99"); 1575 1576 // Loop over all the points in this curve. 1577 for (int pidx = 0; pidx < npts; ++pidx) { 1578 GeomPoint p = crv.get(pidx); 1579 1580 // Write out the coordinates of the point. 1581 writer.printf("%15g %15g %15g ", p.getValue(0), p.getValue(1), p.getValue(2)); 1582 1583 // Write out the parametric coordinates (if any). 1584 if (p instanceof SubrangePoint) { 1585 SubrangePoint sp = (SubrangePoint)p; 1586 GeomPoint parPos = sp.getParPosition(); 1587 1588 // Get the index of the child object. 1589 ParametricGeometry child = sp.getChild(); 1590 int sidx = surfaces.indexOf(child); 1591 if (sidx < 0) { 1592 writer.println(" -1 -1 0"); 1593 } else { 1594 writer.printf("%15g %15g ", parPos.getValue(0), 1595 (parPos.getPhyDimension() > 1 ? parPos.getValue(1) : 0)); 1596 writer.println(sidx + 1); 1597 } 1598 1599 } else 1600 writer.println(" -1 -1 0"); 1601 } 1602 } // Next cidx 1603 1604 } // end writeCurves() 1605 1606 /** 1607 * Write out patch data to the GTC file. 1608 * 1609 * @param writer The PrintWriter we are writing to the file with. 1610 * @param patches The list of patches to be written out. 1611 * @param surfaces The list of surfaces that a patch may be associated with. 1612 * @param curves The list of curves (PointStrings) that the edges of this patch are 1613 * contained in. 1614 * @throws IOException 1615 */ 1616 private static void writePatches(PrintWriter writer, GeomList<GeomList> patches, GeomList surfaces, 1617 GeomList<PointString<GeomPoint>> curves) throws IOException { 1618 1619 // Write out the PATCHES data. 1620 writer.println("PATCHES"); 1621 int nPatches = patches.size(); 1622 writer.println(nPatches); 1623 writer.print(nPatches); 1624 writer.println(" 0 0 2 2 0 0"); 1625 writer.println("99 99 99 99"); 1626 writer.println("99 99 99 99"); 1627 1628 // Loop over all the patches. 1629 int d3mNum = 1; 1630 for (int pidx = 0; pidx < nPatches; ++pidx) { 1631 GeomList<GeomList> patch = patches.get(pidx); 1632 1633 // Write out the patch number. 1634 writer.print("Patch_Number "); 1635 writer.print(pidx + 1); 1636 1637 // Write out some mystery numbers. 1638 writer.print(" 0 16748573 "); 1639 1640 // Write out the number of edges. 1641 GeomList<PointString> edgeLst = patch.get(1); 1642 int nedges = edgeLst.size(); 1643 writer.print(nedges); writer.print(" "); 1644 1645 // Write out the patch type. 1646 Integer type = (Integer)patch.getUserData("GTC_PatchType"); 1647 if (isNull(type)) 1648 type = -1; 1649 writer.print(type); writer.print(" "); 1650 1651 // Write out the boundary condition type. 1652 Integer BCType = (Integer)patch.getUserData("GTC_BCType"); 1653 if (isNull(BCType)) 1654 BCType = 5; 1655 writer.print(BCType); writer.print(" "); 1656 1657 // Write out the family name. 1658 String family = (String)patch.getUserData("GTC_Family"); 1659 if (isNull(family)) { 1660 family = patch.getName(); 1661 if (isNull(family)) 1662 family = "Addams"; 1663 } 1664 writer.println(family); 1665 1666 // Write out the D3M number. 1667 Boolean isValid = (Boolean)patch.getUserData("GTC_ValidPatch"); 1668 if (isNull(isValid)) 1669 isValid = Boolean.FALSE; 1670 if (isValid) { 1671 writer.print(d3mNum); writer.print(" "); 1672 ++d3mNum; 1673 } else 1674 writer.print("-1 "); 1675 1676 // Write out the number of surfaces associated with this patch (0 or 1). 1677 int nsrfs = patch.get(0).size(); 1678 if (nsrfs > 1) 1679 throw new IOException(MessageFormat.format( 1680 RESOURCES.getString("sourceSizeErr"), nsrfs, pidx + 1)); 1681 writer.print(nsrfs); 1682 writer.println(" 99 99"); 1683 1684 if (nsrfs > 0) { 1685 // Write out the surface information. 1686 ParametricGeometry srf = (ParametricGeometry)patch.get(0).get(0); 1687 int sidx = surfaces.indexOf(srf); 1688 if (sidx < 0) 1689 throw new IOException(MessageFormat.format( 1690 RESOURCES.getString("missingPatchSrf"), pidx + 1)); 1691 writer.print(sidx + 1); writer.print(" "); 1692 1693 // Write out the display path. 1694 boolean isCrv = false; 1695 int k1, k2; 1696 if (srf instanceof NurbsCurve) { 1697 isCrv = true; 1698 NurbsCurve ncrv = (NurbsCurve)srf; 1699 k1 = ncrv.getControlPoints().size(); 1700 k2 = 2; 1701 } else { 1702 NurbsSurface nsrf = (NurbsSurface)srf; 1703 k1 = nsrf.getNumberOfRows(); 1704 k2 = nsrf.getNumberOfColumns(); 1705 } 1706 if (isCrv) { 1707 if (k1 < 2) 1708 k1 = 3; 1709 k2 = 2; 1710 } else { 1711 if (k1 < 2) 1712 k1 = 3; 1713 if (k2 < 2) 1714 k2 = 3; 1715 } 1716 writer.print(k1); writer.print(" "); 1717 writer.print(k2); writer.print(" "); 1718 1719 // Write out two mystery numbers. 1720 writer.println("1 1000"); 1721 1722 // Write out the min/max U,V for the surface. 1723 writer.println(" 0 1 0 1"); 1724 1725 // Write out the patch projection parameters. 1726 writer.println(" 0.0001 0.0001 0.100000 1e-05"); 1727 } 1728 1729 // Get the side indexes and reversed edge flags. 1730 List<Integer> sideLst = (List<Integer>)patch.getUserData("GTC_EdgeSideIndexes"); 1731 List<Boolean> reversedLst = (List<Boolean>)patch.getUserData("GTC_EdgeReversedFlags"); 1732 1733 // Loop over the edges. 1734 int prevSide = 0; 1735 for (int eidx = 0; eidx < nedges; ++eidx) { 1736 PointString edge = edgeLst.get(eidx); 1737 int ip1 = eidx + 1; 1738 1739 // Write the edge index. 1740 writer.print(" "); 1741 writer.print(ip1); 1742 1743 // Write out the edge reversed flag. 1744 String reversed = " 1 "; 1745 if (nonNull(reversedLst) && reversedLst.get(eidx)) 1746 reversed = " -1 "; 1747 writer.print(reversed); 1748 1749 // Write out the edge curve number 1750 int cidx = curves.indexOf(edge); 1751 if (cidx < 0) 1752 throw new IOException(MessageFormat.format( 1753 RESOURCES.getString("missingPatchEdge"), ip1, pidx + 1)); 1754 writer.print(cidx + 1); 1755 1756 // Write out the side indicator. 1757 int side = ip1; 1758 if (nonNull(sideLst)) 1759 side = sideLst.get(eidx); 1760 if (side != prevSide || eidx == 0) 1761 writer.print(" 1 "); 1762 else 1763 writer.print(" -1 "); 1764 prevSide = side; 1765 writer.println("99"); 1766 } 1767 } // Next pidx 1768 1769 } // end writePatches() 1770 1771}