From a951b5c14aa987822fdc90af169bd33ef3dde038 Mon Sep 17 00:00:00 2001 From: Lorenz Stechauner Date: Tue, 7 Jun 2022 14:36:07 +0200 Subject: [PATCH] AB8/3 + AB8/4 --- src/Body.java | 13 ++++ src/NamedBody.java | 46 +++++-------- src/NamedBodyForcePair.java | 6 +- src/ReadDataUtil.java | 103 +++++++++++++++++++++++++++- src/Simulation8.java | 102 +++++++++++++++++++++++---- src/StateFileFormatException.java | 15 +++- src/StateFileNotFoundException.java | 9 ++- 7 files changed, 244 insertions(+), 50 deletions(-) diff --git a/src/Body.java b/src/Body.java index abf74b4..e1eda35 100644 --- a/src/Body.java +++ b/src/Body.java @@ -20,6 +20,19 @@ public class Body implements Massive { this.currentMovement = new Vector3(other.currentMovement); } + public Body(double mass) { + this(mass, new Vector3(), new Vector3()); + } + + public Body() { + this(0); + } + + public void setState(Vector3 position, Vector3 velocity) { + this.massCenter = new Vector3(position); + this.currentMovement = new Vector3(velocity); + } + /** * Returns the distance between the mass centers of this body and the specified body 'b'. */ diff --git a/src/NamedBody.java b/src/NamedBody.java index 13e3899..19b02f1 100644 --- a/src/NamedBody.java +++ b/src/NamedBody.java @@ -1,43 +1,41 @@ -import codedraw.CodeDraw; -public class NamedBody implements Massive { +public class NamedBody extends Body { private final String name; - private final Body body; /** * Initializes this with name, mass, current position and movement. */ public NamedBody(String name, double mass, Vector3 massCenter, Vector3 currentMovement) { - this(name, new Body(mass, massCenter, currentMovement)); + super(mass, massCenter, currentMovement); + this.name = name; + } + + public NamedBody(String name, double mass) { + super(mass); + this.name = name; + } + + public NamedBody(String name) { + super(); + this.name = name; } public NamedBody(String name, Body body) { + super(body); this.name = name; - this.body = body; } public NamedBody(NamedBody other) { - this(other.name, new Body(other.body)); + super(other); + this.name = other.name; } /** * Returns the name of the body. */ public String getName() { - return name; - } - - public Body getBody() { - return body; - } - - public Vector3 getMassCenter() { - return body.getMassCenter(); - } - - public double getMass() { - return body.getMass(); + return this.name; } /** @@ -66,14 +64,4 @@ public class NamedBody implements Massive { public String toString() { return this.getName(); } - - @Override - public void move(Vector3 force) { - body.move(force); - } - - @Override - public void draw(CodeDraw cd) { - body.draw(cd); - } } diff --git a/src/NamedBodyForcePair.java b/src/NamedBodyForcePair.java index b2e0392..c148b38 100644 --- a/src/NamedBodyForcePair.java +++ b/src/NamedBodyForcePair.java @@ -30,7 +30,7 @@ public class NamedBodyForcePair implements CosmicSystem { } public Body getBody() { - return body.getBody(); + return body; } /** @@ -72,13 +72,13 @@ public class NamedBodyForcePair implements CosmicSystem { @Override public void addForceTo(CosmicSystem cs) { - cs.addForceFrom(body.getBody()); + cs.addForceFrom(body); } @Override public BodyLinkedList getBodies() { BodyLinkedList list = new BodyLinkedList(); - list.addFirst(body.getBody()); + list.addFirst(body); return list; } diff --git a/src/ReadDataUtil.java b/src/ReadDataUtil.java index e177386..0186fd2 100644 --- a/src/ReadDataUtil.java +++ b/src/ReadDataUtil.java @@ -1,7 +1,14 @@ import java.io.*; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class ReadDataUtil { + private static final Pattern LINE_FORMAT = Pattern.compile("^\\d+(\\.\\d+)?, *[A-Za-z \\d:.-]+(, *[-+]?\\d+(\\.\\d+)?([eE][+-]?\\d+)?){6}, *$"); + private static final Pattern DATE_COLUMN_FORMAT = Pattern.compile("^A\\.D\\. (?\\d{4})-(?[A-Z][a-z]{2})-(?[0-3]\\d) \\d{2}:\\d{2}:\\d{2}(\\.\\d+)?$"); + private static final Pattern DATE_FORMAT_YYYY_MMM_DD = Pattern.compile("^(?\\d{4})-(?[A-Z][a-z]{2})-(?[0-3]\\d)$"); + /** * Reads the position and velocity vector on the specified 'day' from the file with the * specified 'path', and sets position and current velocity of 'b' accordingly. If @@ -20,7 +27,101 @@ public class ReadDataUtil { * Precondition: b != null, path != null, day != null and has the format YYYY-MM-DD. */ public static boolean readConfiguration(NamedBody b, String path, String day) throws IOException { - // TODO: implement this method. + State state = State.Pre; + try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(path))) { + Scanner lines = new Scanner(in); + long lineNum = 0; + while (lines.hasNextLine() && state != State.Post) { + lineNum++; + String line = lines.nextLine(); + State nextState = state.next(line); + + if (state == State.In && nextState == State.In) { + Matcher m = LINE_FORMAT.matcher(line); + if (!m.matches()) { + throw new StateFileFormatException(path, lineNum); + } + + String[] rows = line.split(", *"); + String date; + try { + date = convertDateColumn(rows[1]); + } catch (IllegalArgumentException e) { + throw new StateFileFormatException(path, lineNum); + } + if (date.equals(day)) { + try { + double x = Double.parseDouble(rows[2]); // [km] + double y = Double.parseDouble(rows[3]); // [km] + double z = Double.parseDouble(rows[4]); // [km] + double vx = Double.parseDouble(rows[5]); // [km/s] + double vy = Double.parseDouble(rows[6]); // [km/s] + double vz = Double.parseDouble(rows[7]); // [km/s] + b.setState(new Vector3(x * 1000, y * 1000, z * 1000), new Vector3(vx * 1000, vy * 1000, vz * 1000)); + } catch (NumberFormatException e) { + throw new StateFileFormatException(path, lineNum); + } + return true; + } + } + + state = nextState; + } + } catch (IOException e) { + if (e instanceof FileNotFoundException) { + throw new StateFileNotFoundException(path); + } else { + throw e; + } + } return false; } + + private static String convertDateColumn(String column) { + Matcher m = DATE_COLUMN_FORMAT.matcher(column); + if (!m.matches()) { + throw new IllegalArgumentException(); + } + + return m.group("year") + "-" + convertMonth(m.group("month")) + "-" + m.group("day"); + } + + public static String convertDate(String date) { + Matcher m = DATE_FORMAT_YYYY_MMM_DD.matcher(date); + if (!m.matches()) { + throw new IllegalArgumentException(); + } + return m.group("year") + "-" + convertMonth(m.group("month")) + "-" + m.group("day"); + } + + private static String convertMonth(String month) { + return switch (month) { + case "Jan" -> "01"; + case "Feb" -> "02"; + case "Mar" -> "03"; + case "Apr" -> "04"; + case "May" -> "05"; + case "Jun" -> "06"; + case "Jul" -> "07"; + case "Aug" -> "08"; + case "Sep" -> "09"; + case "Oct" -> "10"; + case "Nov" -> "11"; + case "Dec" -> "12"; + default -> throw new IllegalArgumentException(); + }; + } + + private enum State { + Pre, In, Post; + + public State next(String line) { + switch (this) { + case Pre: if (line.equals("$$SOE")) return In; break; + case In: if (line.equals("$$EOE")) return Post; break; + case Post: break; + } + return this; + } + } } diff --git a/src/Simulation8.java b/src/Simulation8.java index c6349d2..bead110 100644 --- a/src/Simulation8.java +++ b/src/Simulation8.java @@ -1,5 +1,8 @@ import codedraw.CodeDraw; +import java.awt.*; +import java.io.IOException; + /** * Simulates the solar system. */ @@ -18,31 +21,100 @@ public class Simulation8 { // The main simulation method using instances of other classes. public static void main(String[] args) { + if (args.length != 2) { + System.err.println("Error: wrong number of arguments."); + System.exit(1); + } + + String statePath = args[0]; + String date; + try { + date = ReadDataUtil.convertDate(args[1]); + } catch (IllegalArgumentException e) { + System.err.println("Error: State has wrong format (requires YYYY-MMM-DD), aborting."); + System.exit(2); + return; + } // simulation CodeDraw cd = new CodeDraw(); // create solar system with 13 bodies - MassiveForceTreeMap map = new MassiveForceTreeMap(); + MassiveForceTreeMap forceOnBody = new MassiveForceTreeMap(); - map.put(new NamedBody("Oumuamua", 8e6, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Earth", 5.972E24, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Moon", 7.349E22, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Mars1", 6.41712E23, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Deimos", 1.8E20, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Phobos", 1.08E20, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Mercury", 3.301E23, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Venus", 4.86747E24, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Vesta", 2.5908E20, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Pallas", 2.14E20, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Hygiea", 8.32E19, new Vector3(), new Vector3()), new Vector3()); - map.put(new NamedBody("Ceres1", 9.394E20, new Vector3(), new Vector3()), new Vector3()); + forceOnBody.put(new NamedBody("Oumuamua", 8e6), new Vector3()); + forceOnBody.put(new NamedBody("Earth", 5.972E24), new Vector3()); + forceOnBody.put(new NamedBody("Moon", 7.349E22), new Vector3()); + forceOnBody.put(new NamedBody("Mars", 6.41712E23), new Vector3()); + forceOnBody.put(new NamedBody("Deimos", 1.8E20), new Vector3()); + forceOnBody.put(new NamedBody("Phobos", 1.08E20), new Vector3()); + forceOnBody.put(new NamedBody("Mercury", 3.301E23), new Vector3()); + forceOnBody.put(new NamedBody("Venus", 4.86747E24), new Vector3()); + forceOnBody.put(new NamedBody("Vesta", 2.5908E20), new Vector3()); + forceOnBody.put(new NamedBody("Pallas", 2.14E20), new Vector3()); + forceOnBody.put(new NamedBody("Hygiea", 8.32E19), new Vector3()); + forceOnBody.put(new NamedBody("Ceres", 9.394E20), new Vector3()); + MassiveIterator iter = forceOnBody.getKeys().iterator(); + while (iter.hasNext()) { + Massive a = iter.next(); + if (a instanceof NamedBody b) { + boolean remove = false; - //TODO: implementation of this method according to 'Aufgabenblatt8.md'. + try { + boolean found = ReadDataUtil.readConfiguration(b, statePath + "/" + b.getName() + ".txt", date); + if (!found) { + System.err.println("Warning: State not available for " + b.getName() + "."); + remove = true; + } + } catch (IOException e) { + if (e instanceof StateFileNotFoundException notFound) { + System.err.println("Warning: " + notFound.getMessage()); + } else if (e instanceof StateFileFormatException format) { + System.err.println("Warning: " + format.getMessage()); + } else { + System.err.println("Error: " + e.getMessage()); + System.exit(3); + } + remove = true; + } + + if (remove) { + System.err.println("Running simulation without " + b.getName()); + iter.remove(); + } + } + } // add sun after states have been read from files. - map.put(new NamedBody("Sun", 1.989E30, new Vector3(), new Vector3()), new Vector3()); + forceOnBody.put(new NamedBody("Sun", 1.989E30), new Vector3()); + System.out.println("Starting simulation"); + long seconds = 0; + while (true) { + seconds++; + + for (Massive b1 : forceOnBody.getKeys()) { + Vector3 force = new Vector3(); + for (Massive b2 : forceOnBody.getKeys()) { + if (b1 != b2) { + force = force.plus(b1.gravitationalForce(b2)); + } + } + forceOnBody.put(b1, force); + } + + for (Massive body : forceOnBody.getKeys()) { + body.move(forceOnBody.get(body)); + } + + if ((seconds % 3600) == 0) { + cd.clear(Color.BLACK); + for (Massive body : forceOnBody.getKeys()) { + body.draw(cd); + } + cd.show(); + } + } } } diff --git a/src/StateFileFormatException.java b/src/StateFileFormatException.java index 158546d..9888fef 100644 --- a/src/StateFileFormatException.java +++ b/src/StateFileFormatException.java @@ -1,7 +1,20 @@ import java.io.IOException; public class StateFileFormatException extends IOException { + private final String fileName; + private final long lineNum; - // TODO: implement class + public StateFileFormatException(String fileName, long lineNum) { + super("File " + fileName + " has illegal format (line " + lineNum + ")"); + this.fileName = fileName; + this.lineNum = lineNum; + } + public String getFileName() { + return this.fileName; + } + + public long getLineNum() { + return this.lineNum; + } } diff --git a/src/StateFileNotFoundException.java b/src/StateFileNotFoundException.java index 15530ab..d6199e2 100644 --- a/src/StateFileNotFoundException.java +++ b/src/StateFileNotFoundException.java @@ -1,7 +1,14 @@ import java.io.IOException; public class StateFileNotFoundException extends IOException { + private final String fileName; - // TODO: implement class + public StateFileNotFoundException(String fileName) { + super("File " + fileName + " not found."); + this.fileName = fileName; + } + public String getFileName() { + return this.fileName; + } }