surface-tension-calculator/lib/scripts/compute_st.dart

362 lines
11 KiB
Dart

import 'dart:io';
import 'dart:math';
import 'package:scidart/scidart.dart';
import 'extract_edge_coordinate.dart';
import 'find_optimal_params_bashforthadams.dart';
import 'juszas_equation.dart';
import 'minimize_params_optimization.dart';
void computeST(List<String> sysArgs) {
// Initial variable definitions
String imageFilePath = "";
String outputFileName = "";
String posPointsFileName = "";
String negPointsFileName = "";
int processCount = 2;
double syringeWidth = 0.70 * 1.15;
double minaRadcurv = 0.0001;
double maxaRadcurv = 2.0001;
double minbShapefact = 0.0001;
double maxbShapefact = 1.0001;
double minX = 0.0001;
double maxX = 0.5001;
double mintAngle = 0.0001;
double maxtAngle = 0.5001;
int aStep = 20;
int bStep = 20;
int xStep = 5;
int tStep = 5;
int imageFlag = 0;
int posFileFlag = 0;
int negFileFlag = 0;
int outFlag = 0;
int counter = 0;
int solveMethod = 0;
// Argument processing loop.
while (counter <= sysArgs.length - 1) {
String currentOption = sysArgs[counter];
String currentValue = "";
try {
currentValue = sysArgs[counter + 1];
} catch (e) {
exit(0);
}
if (currentOption == "-help") {
exit(0);
} else if (currentOption == "-img") {
imageFilePath = currentValue;
imageFlag = 1;
} else if (currentOption == "-filep") {
posPointsFileName = currentValue;
posFileFlag = 1;
} else if (currentOption == "-filen") {
negPointsFileName = currentValue;
negFileFlag = 1;
} else if (currentOption == "-out") {
outputFileName = currentValue;
outFlag = 1;
} else if (currentOption == "-proc") {
processCount = int.parse(currentValue);
} else if (currentOption == "-syrg") {
syringeWidth = double.parse(currentValue);
} else if (currentOption == "-a") {
minaRadcurv = double.parse(currentValue);
} else if (currentOption == "-A") {
maxaRadcurv = double.parse(currentValue);
} else if (currentOption == "-b") {
minbShapefact = double.parse(currentValue);
} else if (currentOption == "-B") {
maxbShapefact = double.parse(currentValue);
} else if (currentOption == "-x") {
minX = double.parse(currentValue);
} else if (currentOption == "-X") {
maxX = double.parse(currentValue);
} else if (currentOption == "-t") {
mintAngle = double.parse(currentValue);
} else if (currentOption == "-T") {
maxtAngle = double.parse(currentValue);
} else if (currentOption == "-ai") {
aStep = int.parse(currentValue);
} else if (currentOption == "-bi") {
bStep = int.parse(currentValue);
} else if (currentOption == "-xi") {
xStep = int.parse(currentValue);
} else if (currentOption == "-ti") {
tStep = int.parse(currentValue);
} else if (currentOption == "-solver") {
solveMethod = int.parse(currentValue);
} else {
exit(0);
}
counter = counter + 2;
}
if (outFlag == 0 && imageFlag == 1) {
print("Output file name not set. (-out)");
exit(0);
}
if (imageFlag == 1 && ((posFileFlag == 1) || (negFileFlag == 1))) {
print("Image(-img) can not be set with files option (-filep, -filen).");
print("Either set image only or files only.");
exit(0);
}
if (imageFlag == 0 &&
((posFileFlag == 1 && negFileFlag == 0) ||
(posFileFlag == 0 && negFileFlag == 1))) {
print(
"File inputs (-filep, -filen) should both be set if image input is not set.",
);
exit(0);
}
// Calculate ARange_radCurv
List<double> ARange_radCurv = [];
double a = minaRadcurv;
double ahop = (maxaRadcurv - minaRadcurv) / (aStep - 1);
while (a <= maxaRadcurv) {
ARange_radCurv.add(a);
a += ahop;
if (ahop == 0.0) {
break;
}
}
if ((maxaRadcurv - ARange_radCurv[ARange_radCurv.length - 1] - ahop).abs() <
0.000001 &&
ahop != 0.0) {
ARange_radCurv.add(maxaRadcurv);
}
// Calculate bRange_shapeFact
List<double> brangeShapefact = [];
double b = minbShapefact;
double bhop = (maxbShapefact - minbShapefact) / (bStep - 1);
while (b <= maxbShapefact) {
brangeShapefact.add(b);
b += bhop;
if (bhop == 0) {
break;
}
}
if ((maxbShapefact - brangeShapefact[brangeShapefact.length - 1] - bhop)
.abs() <
0.000001 &&
bhop != 0.0) {
brangeShapefact.add(maxbShapefact);
}
// Calculate XRange
List<double> XRange = [];
double x = minX;
double xhop = (maxX - minX) / (xStep - 1);
while (x <= maxX) {
XRange.add(x);
x += xhop;
if (xhop == 0) {
break;
}
}
if ((maxX - XRange[XRange.length - 1] - xhop).abs() < 0.000001 &&
xhop != 0.0) {
XRange.add(maxX);
}
// Calculate TRange_angle
List<double> TRange_angle = [];
double t = mintAngle;
double thop = (maxtAngle - mintAngle) / (tStep - 1);
while (t <= maxtAngle) {
TRange_angle.add(t);
t += thop;
if (thop == 0.0) {
break;
}
}
if ((maxtAngle - TRange_angle[TRange_angle.length - 1] - thop).abs() <
0.000001 &&
thop != 0.0) {
TRange_angle.add(maxtAngle);
}
print("\n");
if (imageFlag == 1) {
print("Image File : $imageFilePath");
print("Output Name : $outputFileName");
print("Negative Coordinate File : ${outputFileName}_Negative.dat");
print("Positive Coordinate File : ${outputFileName}_Positive.dat");
print("Syringe Width (millimeter): $syringeWidth");
} else {
print("Negative Coordinate File : $negPointsFileName");
print("Positive Coordinate File : $posPointsFileName");
}
print(
"Radius of Curvature Range : $minaRadcurv-$maxaRadcurv (interval: ${ARange_radCurv.length})",
);
print(
"Shape Factor Range : $minbShapefact-$maxbShapefact (interval: ${brangeShapefact.length})",
);
print("Initial X Coordinate Range: $minX-$maxX (interval: ${XRange.length})");
print(
"Initial Angle Range : $mintAngle-$maxtAngle (interval: ${TRange_angle.length})",
);
print(
"Parameter Combinations : ${ARange_radCurv.length * brangeShapefact.length * XRange.length * TRange_angle.length}",
);
print("Number of Process to Use : $processCount");
List<List<double>> posEdgePts;
List<List<double>> negEdgePts;
if (imageFlag == 1) {
var result = extractEdgeCoordinate(
imageFilePath,
outputFileName,
syringeWidth,
);
posEdgePts = result[0];
negEdgePts = result[1];
double deScaledMaxWidth = result[2];
List<double> dmsDiameters = List<double>.from(result[3]);
double HInv1 = HJuza(dmsDiameters[7] / deScaledMaxWidth, 0.8);
double HInv2 = HJuza(dmsDiameters[8] / deScaledMaxWidth, 0.9);
double HInv3 = HJuza(dmsDiameters[9] / deScaledMaxWidth, 1.0);
double HInv4 = HJuza(dmsDiameters[10] / deScaledMaxWidth, 1.1);
double HInv5 = HJuza(dmsDiameters[11] / deScaledMaxWidth, 1.2);
double HInvM = (0.20) * (HInv1 + HInv2 + HInv3 + HInv4 + HInv5);
double HInvSD = sqrt(
(0.20) *
((HInv1 - HInvM) * (HInv1 - HInvM) +
(HInv2 - HInvM) * (HInv2 - HInvM) +
(HInv3 - HInvM) * (HInv3 - HInvM) +
(HInv4 - HInvM) * (HInv4 - HInvM) +
(HInv5 - HInvM) * (HInv5 - HInvM)),
);
print("Juza 5-Plane (1/H) Std.Dev: $HInvSD");
} else {
posEdgePts = [];
try {
List<String> positiveLines = File(posPointsFileName).readAsLinesSync();
for (var Line in positiveLines) {
List<String> coordinate = Line.split(RegExp(r'\s+'));
//print(coordinate);
posEdgePts.add([
double.parse(coordinate[0]),
double.parse(coordinate[1]),
]);
}
} catch (e) {
print("Error reading positive points file.");
exit(0);
}
negEdgePts = [];
try {
List<String> negativeLines = File(negPointsFileName).readAsLinesSync();
for (var Line in negativeLines) {
List<String> coordinate = Line.split(RegExp(r'\s+'));
//print(Coordinate);
negEdgePts.add([
double.parse(coordinate[0]),
double.parse(coordinate[1]),
]);
}
} catch (e) {
print("Error reading negative points file.");
exit(0);
}
}
// Nested function bruteForce
List<dynamic> bruteForce() {
List<List<double>> posList = [];
List<List<double>> negList = [];
for (int processNo = 0; processNo < processCount; processNo++) {
posList.add(posEdgePts);
negList.add(negEdgePts);
//print(processNo);
}
List<List<double>> aRanges = [];
for (int processNo = 0; processNo < processCount; processNo++) {
aRanges.add([]);
}
int i = 0;
for (var A in ARange_radCurv) {
aRanges[i % processCount].add(A);
i += 1;
}
List<List<dynamic>> bestParams = []; // will store candidate tuples
List<int> ticksQueue = []; // simulate ticks
// Simulate process creation and execution.
for (int processNo = 0; processNo < processCount; processNo++) {
// In a real multiprocessing environment, each process would run concurrently.
// Here, we directly call the function.
findOptimalParameters(
posList[processNo],
negList[processNo],
aRanges[processNo],
brangeShapefact,
XRange,
TRange_angle,
bestParams,
ticksQueue,
);
}
stdout.write("\rParameter Search Progress : 0%");
stdout.flush();
int count = 0;
int totalTicks = ARange_radCurv.length;
// Simulate receiving ticks.
while (count < totalTicks) {
// Since ticks have been already added, just increment Count.
if (ticksQueue.isNotEmpty) {
ticksQueue.removeAt(0);
count += 1;
stdout.write(
"\rParameter Search Progress : ${(100 * (count / totalTicks)).toInt()}%",
);
stdout.flush();
}
}
print("");
List<dynamic> bestOutput = [999999999, 1, 1, 1, 1];
for (var output in bestParams) {
if (output[0] < bestOutput[0]) {
bestOutput = output;
}
}
return bestOutput;
}
// Nested function Optimize
List<dynamic> optimize() {
return optimizeParameters(posEdgePts, negEdgePts, 0.95, 0.3, 0.1, 0.2);
}
// Record start time
var startTime = DateTime.now();
// Choose which solver to use.
List<dynamic> bestOutput = (solveMethod != 1) ? optimize() : bruteForce();
print("best output: $bestOutput");
print("\nLowest RMSD : ${bestOutput[0]}");
// Computed Surface Tension: 9.8*bestOutput[1]/bestOutput[2]
print("Computed Surface Tension : ${9.8 * bestOutput[1] / bestOutput[2]}");
print("Best Radius of Curvature : ${bestOutput[1]}");
print("Best Shape Factor : ${bestOutput[2]}");
print("Best Initial X Coordinate : ${bestOutput[3]}");
print("Best Initial Angle : ${bestOutput[4]}");
var elapsed = DateTime.now().difference(startTime).inSeconds;
print("Lapsed Time in Seconds : $elapsed");
}