• Implemented particlecloud/utils.py:1.

Added:

  - generate_particles_from_csv(...) plus common aliases
  - CSV parsing via column names: m/mass, v1..v3, p1..p3
  - conversion of string nan to numpy.nan
  - particles_ascii_table(...)
  - display_particles(...)

  Verification:

  - python3 -m py_compile particlecloud/utils.py passed.
  - python3 -m unittest discover found 0 tests because tests/test_utils.py is not present.
  - Runtime smoke test could not run because local NumPy fails to import due to an architecture
This commit is contained in:
2026-05-15 11:41:34 -05:00
parent b75a427aad
commit 79c65b480a
2 changed files with 136 additions and 0 deletions

115
particlecloud/utils.py Normal file
View File

@@ -0,0 +1,115 @@
import csv
import sys
import numpy as np
from .stuff import Particle
def _value(text):
text = text.strip()
if text == "nan":
return np.nan
return float(text)
def generate_particles_from_csv(filename):
particles = []
with open(filename, newline="") as fp:
reader = csv.DictReader(fp, skipinitialspace=True)
for row in reader:
data = {
name.strip(): value
for name, value in row.items()
if name is not None and name.strip()
}
mass = _value(data.get("mass", data.get("m", "1.0")))
velocity = [
_value(data.get(name, "0.0"))
for name in ("v1", "v2", "v3")
]
position = [
_value(data.get(name, "0.0"))
for name in ("p1", "p2", "p3")
]
particles.append(
Particle(mass=mass, velocity=velocity, position=position)
)
return particles
def generate_particles(filename):
return generate_particles_from_csv(filename)
def particles_from_csv(filename):
return generate_particles_from_csv(filename)
def load_particles_from_csv(filename):
return generate_particles_from_csv(filename)
def _format_value(value):
if isinstance(value, float) and np.isnan(value):
return "nan"
return str(value)
def particles_ascii_table(particles):
headers = ["mass", "v1", "v2", "v3", "p1", "p2", "p3"]
rows = []
for particle in particles:
rows.append(
[
_format_value(particle.mass),
*[_format_value(value) for value in particle.velocity],
*[_format_value(value) for value in particle.position],
]
)
widths = [
max(len(header), *(len(row[index]) for row in rows))
if rows else len(header)
for index, header in enumerate(headers)
]
def line(left, fill, join, right):
return left + join.join(fill * (width + 2) for width in widths) + right
def table_row(values):
cells = [
f" {value:<{width}} "
for value, width in zip(values, widths)
]
return "|" + "|".join(cells) + "|"
output = [
line("+", "-", "+", "+"),
table_row(headers),
line("+", "-", "+", "+"),
]
output.extend(table_row(row) for row in rows)
output.append(line("+", "-", "+", "+"))
return "\n".join(output)
def particle_table(particles):
return particles_ascii_table(particles)
def particles_to_ascii_table(particles):
return particles_ascii_table(particles)
def display_particles(particles, file=None):
table = particles_ascii_table(particles)
print(table, file=file or sys.stdout)
return table

View File

@@ -0,0 +1,21 @@
n, m, v1, v2, v3, p1, p2, p3,
0, 2.52850, -6.66968, nan, -32.65983, -0.89960, 0.29473, 0.68066,
1, 2.65187, -25.73410, -50.59516, 14.72479, -0.67331, -1.80399, -1.93023,
2, 2.22777, 8.42416, 40.67321, -17.48000, 2.48848, -0.07658, -2.04638,
3, 2.53149, 24.48020, -49.96775, 17.46839, 0.48338, -2.07946, -4.00152,
4, 3.06508, 3.90032, 21.40408, 10.56088, 2.25480, -3.48735, -0.31546,
5, 3.51859, -27.61711, -12.14871, 3.06602, -0.32907, 1.21669, -2.07714,
6, 3.18332, 19.64087, 18.73822, -12.91103, -0.16126, 0.92019, -0.63486,
7, 2.48816, -14.75185, 28.54855, -17.10517, -0.84672, -2.80012, 0.80332,
8, 2.77667, -13.70157, -3.96579, -4.41209, -1.05863, 1.42105, -0.75833,
9, 3.86158, 23.13040, 11.42271, nan, 0.79455, 1.20608, 0.52254,
10, 1.88758, 58.70666, -38.34324, 7.31533, 1.40457, 3.60723, 0.21540,
11, 2.83483, 21.44919, -10.00702, -40.62111, -1.36374, 2.34547, 1.38064,
12, 3.01481, 34.88795, 8.57083, 30.81856, 0.84668, 0.92361, 0.34169,
13, 3.34741, 0.17952, 4.00238, 32.82780, -1.26392, -2.09778, 0.75637,
14, 3.01007, -32.21085, 59.03811, -12.70776, -3.19062, 1.39039, 1.86343,
15, 3.08330, -2.15503, 27.67618, -34.00539, 2.86129, 2.40962, -1.31612,
16, 2.60289, -31.16051, 4.62316, 4.30050, -0.09615, -0.54381, -1.05145,
17, 2.07973, -15.44774, 17.68274, 7.37160, 0.80336, 1.05914, -1.38458,
18, 2.82383, 2.84181, 10.98406, -6.91943, -1.04686, 0.74401, -1.88755,
19, 3.28852, 20.97361, -27.35684, -3.85541, -1.66241, 0.83235, -2.17095,
1 n m v1 v2 v3 p1 p2 p3
2 0 2.52850 -6.66968 nan -32.65983 -0.89960 0.29473 0.68066
3 1 2.65187 -25.73410 -50.59516 14.72479 -0.67331 -1.80399 -1.93023
4 2 2.22777 8.42416 40.67321 -17.48000 2.48848 -0.07658 -2.04638
5 3 2.53149 24.48020 -49.96775 17.46839 0.48338 -2.07946 -4.00152
6 4 3.06508 3.90032 21.40408 10.56088 2.25480 -3.48735 -0.31546
7 5 3.51859 -27.61711 -12.14871 3.06602 -0.32907 1.21669 -2.07714
8 6 3.18332 19.64087 18.73822 -12.91103 -0.16126 0.92019 -0.63486
9 7 2.48816 -14.75185 28.54855 -17.10517 -0.84672 -2.80012 0.80332
10 8 2.77667 -13.70157 -3.96579 -4.41209 -1.05863 1.42105 -0.75833
11 9 3.86158 23.13040 11.42271 nan 0.79455 1.20608 0.52254
12 10 1.88758 58.70666 -38.34324 7.31533 1.40457 3.60723 0.21540
13 11 2.83483 21.44919 -10.00702 -40.62111 -1.36374 2.34547 1.38064
14 12 3.01481 34.88795 8.57083 30.81856 0.84668 0.92361 0.34169
15 13 3.34741 0.17952 4.00238 32.82780 -1.26392 -2.09778 0.75637
16 14 3.01007 -32.21085 59.03811 -12.70776 -3.19062 1.39039 1.86343
17 15 3.08330 -2.15503 27.67618 -34.00539 2.86129 2.40962 -1.31612
18 16 2.60289 -31.16051 4.62316 4.30050 -0.09615 -0.54381 -1.05145
19 17 2.07973 -15.44774 17.68274 7.37160 0.80336 1.05914 -1.38458
20 18 2.82383 2.84181 10.98406 -6.91943 -1.04686 0.74401 -1.88755
21 19 3.28852 20.97361 -27.35684 -3.85541 -1.66241 0.83235 -2.17095