diff --git a/src/scripts/config_gen.yml b/src/scripts/config_gen.yml deleted file mode 100644 index f1742a04c8a8e56f6faf64c5333c238b8a194fe7..0000000000000000000000000000000000000000 --- a/src/scripts/config_gen.yml +++ /dev/null @@ -1,93 +0,0 @@ -system_settings: - max_host_ram : 0 - max_gpu_ram : 0 - -input_settings: - input_folder : "." - spheres_file : "DEDFB" - geometry_file: "DCLU" - -output_settings: - output_folder: "." - output_name : "c_OCLU" - formats : [ "LEGACY", "HDF5" ] - jwtm : 1 - -particle_settings: - application : "CLUSTER" - n_spheres : 8 - n_types : 2 - sph_types : [ 1, 1, 1, 1, 2, 2, 2, 2 ] - n_layers : [ 1, 1 ] - radii : [ 6.000e-08, 4.000e-8 ] - rad_frac : [ [ 1.0 ], [ 1.0 ] ] - dielec_id : [ [ 1 ], [ 1 ] ] - -material_settings: - diel_flag : 0 - extern_diel : 1.00e+00 - dielec_path : "../../ref_data" - dielec_file : [ "eps_ashok_C.csv" ] - dielec_fmt : [ "CSV" ] - match_mode : "GRID" - diel_const : [ ] - -radiation_settings: - polarization: "LINEAR" - scale_start : 1.00e-06 - scale_end : 2.00e-05 - scale_step : 5.00e-09 - wp : 2.99792e+08 - xip : 1.00e+00 - step_flag : 0 - scale_name : "WAVELENGTH" - -geometry_settings: - li : 4 - le : 8 - npnt : 149 - npntts : 300 - iavm : 0 - isam : 0 - in_th_start : 0.0 - in_th_step : 0.0 - in_th_end : 0.0 - in_ph_start : 0.0 - in_ph_step : 0.0 - in_ph_end : 0.0 - sc_th_start : 0.0 - sc_th_step : 0.0 - sc_th_end : 0.0 - sc_ph_start : 0.0 - sc_ph_step : 0.0 - sc_ph_end : 0.0 - x_coords : [ - 8.00e-08, - -8.00e-08, - 0.00e+00, - 0.00e+00, - 0.00e+00, - 0.00e+00, - 0.00e+00, - 0.00e+00 - ] - y_coords : [ - 0.00e+00, - 0.00e+00, - 8.00e-08, - -8.00e-08, - 0.00e+00, - 0.00e+00, - 0.00e+00, - 0.00e+00 - ] - z_coords : [ - 0.00e+00, - 0.00e+00, - 0.00e+00, - 0.00e+00, - 8.00e-08, - -8.00e-08, - 16.00e-08, - -16.00e-08 - ] diff --git a/src/scripts/generator.py b/src/scripts/generator.py deleted file mode 100644 index 901f58dd2c081ee5e4f4923e0e73f3c266e53ff4..0000000000000000000000000000000000000000 --- a/src/scripts/generator.py +++ /dev/null @@ -1,105 +0,0 @@ -import math -import random -import yaml - -#import pdb - -from pathlib import PurePath -from sys import argv - -seed = int(argv[1]) - -random.seed(seed) - -config = {} -with open('config_rand.yml', 'r') as stream: - config = yaml.safe_load(stream) - -nsph = int(config['particle_settings']['n_spheres']) - -vec_thetas = [0.0 for i in range(nsph)] -vec_phis = [0.0 for i in range(nsph)] -vec_rads = [0.0 for i in range(nsph)] - -n_types = config['particle_settings']['n_types'] -if (len(config['particle_settings']['sph_types']) > 0): - if (len(config['particle_settings']['sph_types']) != nsph): - print("ERROR: declared number of types does not match the number of spheres!") - exit(1) -else: - # Random type generation - for ti in range(nsph): - itype = 1 + int(n_types * random.random()) - config['particle_settings']['sph_types'].append(itype) -sph_type_index = config['particle_settings']['sph_types'][0] - 1 -vec_spheres = [{'itype': sph_type_index + 1, 'x': 0.0, 'y': 0.0, 'z': 0.0}] -vec_rads[0] = config['particle_settings']['radii'][sph_type_index] -max_rad = 20.0 * vec_rads[0] -placed_spheres = 1 -attempts = 0 -max_attempts = 100 -for i in range(1, nsph): - sph_type_index = config['particle_settings']['sph_types'][i] - 1 - vec_rads[i] = config['particle_settings']['radii'][sph_type_index] - is_placed = False - #breakpoint() - while (not is_placed): - if (attempts > max_attempts): - print("WARNING: could not place sphere %d in allowed radius!"%i) - break # while(not is_placed) - vec_thetas[i] = math.pi * random.random() - vec_phis[i] = 2.0 * math.pi * random.random() - rho = vec_rads[0] + vec_rads[i] - z = rho * math.cos(vec_thetas[i]) - y = rho * math.sin(vec_thetas[i]) * math.sin(vec_phis[i]) - x = rho * math.sin(vec_thetas[i]) * math.cos(vec_phis[i]) - j = 0 - while (j < i - 1): - j += 1 - dx2 = (x - vec_spheres[j]['x']) * (x - vec_spheres[j]['x']) - dy2 = (y - vec_spheres[j]['y']) * (y - vec_spheres[j]['y']) - dz2 = (z - vec_spheres[j]['z']) * (z - vec_spheres[j]['z']) - dist2 = dx2 + dy2 + dz2 - rr2 = (vec_rads[i] + vec_rads[j]) * (vec_rads[i] + vec_rads[j]) - if (dist2 < rr2): - # Spheres i and j are compenetrating. - # Sphere i is moved out radially until it becomes externally - # tangent to sphere j. Then the check is repeated, to verify - # that no other sphere was penetrated. The process is iterated - # until sphere i is placed or the maximum allowed radius is - # reached. - sinthi = math.sin(vec_thetas[i]) - sinthj = math.sin(vec_thetas[j]) - costhi = math.cos(vec_thetas[i]) - costhj = math.cos(vec_thetas[j]) - sinphi = math.sin(vec_phis[i]) - sinphj = math.sin(vec_phis[j]) - cosphi = math.cos(vec_phis[i]) - cosphj = math.cos(vec_phis[j]) - cosalpha = ( - sinthi * cosphi * sinthj * cosphj - + sinthi * sinphi * sinthj * sinphj - + costhi * costhj - ) - rho += 2.0 * vec_rads[j] * cosalpha - z = rho * math.cos(vec_thetas[i]) - y = rho * math.sin(vec_thetas[i]) * math.sin(vec_phis[i]) - x = rho * math.sin(vec_thetas[i]) * math.cos(vec_phis[i]) - j = 0 - continue # while(j < i - 1) - if (rho + vec_rads[i] > max_rad): - # The current direction is filled. Try another one. - attempts += 1 - continue # while(not is_placed) - vec_spheres.append({ - 'itype': sph_type_index + 1, - 'x': x, - 'y': y, - 'z': z - }) - is_placed = True - placed_spheres += 1 - attempts = 0 - -print(vec_spheres) -print(sorted(vec_spheres, key=lambda item: item['itype'])) diff --git a/src/scripts/model_maker.py b/src/scripts/model_maker.py index 70b4df231d689d9d03045c0c014e9a923a456023..555e3227a509c48015c5dcf358b075bd2e4bcc0f 100755 --- a/src/scripts/model_maker.py +++ b/src/scripts/model_maker.py @@ -1,4 +1,4 @@ -#!/bin/python3 +#!/usr/bin/env python3 # Copyright (C) 2024 INAF - Osservatorio Astronomico di Cagliari # @@ -24,13 +24,15 @@ # The script requires python3. import math +import os +import pdb import random import yaml allow_3d = True try: import pyvista as pv -except ModuleNotFound as ex: +except ModuleNotFoundError as ex: print("WARNING: pyvista not found!") allow_3d = False @@ -140,6 +142,7 @@ def load_model(model_file): except FileNotFoundError: print("ERROR: " + model_file + " was not found!") if model is not None: + max_rad = 0.0 make_3d = False if model['system_settings']['make_3D'] == "0" else True if (make_3d and not allow_3d): print("WARNING: 3D visualization of models is not available. Disabling.") @@ -363,8 +366,8 @@ def load_model(model_file): # Generate random cluster rnd_seed = int(model['system_settings']['rnd_seed']) # random_aggregate() checks internally whether application is INCLUSION - print("DEBUG: make_3d is %s."%str(make_3d)) - random_aggregate(sconf, gconf, rnd_seed, 20.0 * max(sconf['ros']), make_3d) + max_rad = float(model['particle_settings']['max_rad']) + random_aggregate(sconf, gconf, rnd_seed, max_rad) else: if (len(model['geometry_settings']['x_coords']) != gconf['nsph']): print("ERROR: coordinate vectors do not match the number of spheres!") @@ -373,6 +376,11 @@ def load_model(model_file): gconf['vec_sph_x'][si] = float(model['geometry_settings']['x_coords'][si]) gconf['vec_sph_y'][si] = float(model['geometry_settings']['y_coords'][si]) gconf['vec_sph_z'][si] = float(model['geometry_settings']['z_coords'][si]) + # + if (model['system_settings']['make_3D'] != "0" and allow_3d): + if (max_rad == 0.0): + max_rad = 20.0 * max(sconf['ros']) + write_obj(sconf, gconf, max_rad) else: # model is None print("ERROR: could not parse " + model_file + "!") return (sconf, gconf) @@ -500,8 +508,21 @@ def print_help(): print("--help Print this help and exit.") print(" ") -# random_aggregate() -def random_aggregate(scatterer, geometry, seed, max_rad, make3d=False, max_attempts=100): + +## \brief Generate a random cluster aggregate from YAML configuration options. +# +# This function generates a random aggregate of spheres using radial ejection +# in random directions of new spheres until they become tangent to the +# outermost sphere existing in that direction. The result of the generated +# model is directly saved in the parameters of the scatterer and geometry +# configuration dictionaries. +# +# \param scatterer: `dict` Scatterer configuration dictionary (gets modified) +# \param geometry: `dict` Geometry configuration dictionary (gets modified) +# \param seed: `int` Seed for the random sequence generation +# \param max_rad: `float` Maximum allowed radial extension of the aggregate +# \param max_attempts: `int` Maximum number of attempts to place a particle in any direction +def random_aggregate(scatterer, geometry, seed, max_rad, max_attempts=100): random.seed(seed) nsph = scatterer['nsph'] vec_thetas = [0.0 for i in range(nsph)] @@ -540,13 +561,14 @@ def random_aggregate(scatterer, geometry, seed, max_rad, make3d=False, max_attem dz2 = (z - vec_spheres[j]['z']) * (z - vec_spheres[j]['z']) dist2 = dx2 + dy2 + dz2 rr2 = (vec_rads[i] + vec_rads[j]) * (vec_rads[i] + vec_rads[j]) - if (dist2 < rr2): + if (dist2 < 0.9999 * rr2): # Spheres i and j are compenetrating. # Sphere i is moved out radially until it becomes externally # tangent to sphere j. Then the check is repeated, to verify # that no other sphere was penetrated. The process is iterated # until sphere i is placed or the maximum allowed radius is # reached. + # breakpoint() sinthi = math.sin(vec_thetas[i]) sinthj = math.sin(vec_thetas[j]) costhi = math.cos(vec_thetas[i]) @@ -560,7 +582,17 @@ def random_aggregate(scatterer, geometry, seed, max_rad, make3d=False, max_attem + sinthi * sinphi * sinthj * sinphj + costhi * costhj ) - rho += 2.0 * vec_rads[j] * cosalpha + D12 = math.sqrt( + vec_spheres[j]['x'] * vec_spheres[j]['x'] + + vec_spheres[j]['y'] * vec_spheres[j]['y'] + + vec_spheres[j]['z'] * vec_spheres[j]['z'] + ) + O1K = D12 * cosalpha + sinalpha = math.sqrt(1.0 - cosalpha * cosalpha) + sinbetaprime = D12 / (vec_rads[i] + vec_rads[j]) * sinalpha + cosbetaprime = math.sqrt(1.0 - sinbetaprime * sinbetaprime) + Op3K = (vec_rads[i] + vec_rads[j]) * cosbetaprime + rho = O1K + Op3K z = rho * math.cos(vec_thetas[i]) y = rho * math.sin(vec_thetas[i]) * math.sin(vec_phis[i]) x = rho * math.sin(vec_thetas[i]) * math.cos(vec_phis[i]) @@ -586,17 +618,6 @@ def random_aggregate(scatterer, geometry, seed, max_rad, make3d=False, max_attem geometry['vec_sph_y'][sph_index] = sphere['y'] geometry['vec_sph_z'][sph_index] = sphere['z'] sph_index += 1 - print("DEBUG: make3d is %s"%str(make3d)) - if make3d: - # Do something - for vi in range(len(vec_rads)): - radius = vec_rads[vi] / max_rad - x = vec_spheres[vi]['x'] / max_rad - y = vec_spheres[vi]['y'] / max_rad - z = vec_spheres[vi]['z'] / max_rad - mesh = pv.Sphere(radius, (x, y, z)) - mesh_name = "sphere_{0:04d}.obj".format(vi) - mesh.save(mesh_name) # end random_aggregate() ## \brief Write the geometry configuration dictionary to legacy format. @@ -749,6 +770,67 @@ def write_legacy_sconf(conf): output.close() return result +## \brief Export the model to a set of OBJ files for 3D visualization. +# +# This function exports the model as a set of OBJ files (one for every +# spherical unit, plus a single scene file) to allow for model visualization +# with 3D software tools. +# +# The spherical units are saved as `sphere_XXXX.obj` files. The material +# information is collected in a `model.mtl` file and the whole scene is written +# to a `model.obj` file. +# +# \param scatterer: `dict` Scatterer configuration dictionary (gets modified) +# \param geometry: `dict` Geometry configuration dictionary (gets modified) +# \param max_rad: `float` Maximum allowed radial extension of the aggregate +def write_obj(scatterer, geometry, max_rad): + color_strings = [ + "1.0 1.0 1.0\n", # white + "1.0 0.0 0.0\n", # red + "0.0 0.0 1.0\n", # blue + "0.0 1.0 0.0\n", # green + ] + color_names = [ + "white", "red", "blue", "green" + ] + mtl_file = open("model.mtl", "w") + for mi in range(len(color_strings)): + mtl_line = "newmtl mtl{0:d}\n".format(mi) + mtl_file.write(mtl_line) + color_line = color_strings[mi] + mtl_file.write(" Ka " + color_line) + mtl_file.write(" Ks " + color_line) + mtl_file.write(" Kd " + color_line) + mtl_file.write(" Ns 100.0\n") + mtl_file.write(" Tr 0.0\n") + mtl_file.write(" illum 2\n\n") + mtl_file.close() + pl = pv.Plotter() + for si in range(scatterer['nsph']): + sph_type_index = scatterer['vec_types'][si] + color_by_name = color_names[sph_type_index] + radius = scatterer['ros'][sph_type_index - 1] / max_rad + x = geometry['vec_sph_x'][si] / max_rad + y = geometry['vec_sph_y'][si] / max_rad + z = geometry['vec_sph_z'][si] / max_rad + mesh = pv.Sphere(radius, (x, y, z)) + mesh.save("tmp_mesh.obj") + pl.add_mesh(mesh) #, color=color_by_name) + mesh_name = "sphere_{0:04d}.obj".format(si) + in_obj_file = open("tmp_mesh.obj", "r") + out_obj_file = open(mesh_name, "w") + in_line = in_obj_file.readline() + out_obj_file.write(in_line) + out_obj_file.write("mtllib model.mtl\n") + out_obj_file.write("usemtl mtl{0:d}\n".format(sph_type_index)) + while (in_line != ""): + in_line = in_obj_file.readline() + out_obj_file.write(in_line) + in_obj_file.close() + out_obj_file.close() + pl.export_obj("model.obj") + os.remove("tmp_mesh.obj") + ## \brief Exit code (0 for success) exit_code = main() exit(exit_code)