Skip to content

Commit 3114f81

Browse files
committed
Removed alpha serializer
1 parent 4fe5314 commit 3114f81

File tree

4 files changed

+123
-539
lines changed

4 files changed

+123
-539
lines changed

rocketpy/simulation/monte_carlo.py

Lines changed: 123 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import json
44
import os
55
import pickle
6+
from copy import deepcopy
67
from pathlib import Path
78
from time import process_time, time
89

@@ -18,8 +19,6 @@
1819
from rocketpy.plots.monte_carlo_plots import _MonteCarloPlots
1920
from rocketpy.prints.monte_carlo_prints import _MonteCarloPrints
2021
from rocketpy.simulation.flight import Flight
21-
from rocketpy.simulation.sim_config.flight2serializer import flightv1_serializer
22-
from rocketpy.simulation.sim_config.serializer import function_serializer
2322
from rocketpy.stochastic import (
2423
StochasticEnvironment,
2524
StochasticFlight,
@@ -189,6 +188,9 @@ def simulate(
189188
saved to the output file as a .h5 file. Default is False.
190189
parallel : bool, optional
191190
If True, the simulations will be run in parallel. Default is False.
191+
n_workers : int, optional
192+
Number of workers to be used. If None, the number of workers
193+
will be equal to the number of CPUs available. Default is None.
192194
193195
Returns
194196
-------
@@ -336,8 +338,6 @@ def __run_in_parallel(self, append, light_mode, n_workers=None):
336338

337339
with MonteCarloManager() as manager:
338340
# initialize queue
339-
inputs_lock = manager.Lock()
340-
outputs_lock = manager.Lock()
341341
errors_lock = manager.Lock()
342342

343343
go_write_inputs = [manager.Semaphore(value=1) for _ in range(n_sim_memory)]
@@ -411,6 +411,8 @@ def __run_in_parallel(self, append, light_mode, n_workers=None):
411411
args=(
412412
i,
413413
n_workers,
414+
light_mode,
415+
file_paths,
414416
self.environment,
415417
self.rocket,
416418
self.flight,
@@ -420,13 +422,12 @@ def __run_in_parallel(self, append, light_mode, n_workers=None):
420422
go_write_results,
421423
go_read_inputs,
422424
go_read_results,
423-
light_mode,
424-
file_paths,
425425
shared_inputs_buffer.name,
426426
shared_results_buffer.name,
427427
inputs_size,
428428
results_size,
429429
n_sim_memory,
430+
self.export_sample_time,
430431
),
431432
)
432433
processes.append(p)
@@ -504,6 +505,8 @@ def __run_in_parallel(self, append, light_mode, n_workers=None):
504505
def __run_simulation_worker(
505506
worker_no,
506507
n_workers,
508+
light_mode,
509+
file_paths,
507510
sto_env,
508511
sto_rocket,
509512
sto_flight,
@@ -513,13 +516,12 @@ def __run_simulation_worker(
513516
go_write_results,
514517
go_read_inputs,
515518
go_read_results,
516-
light_mode,
517-
file_paths,
518519
shared_inputs_name,
519520
shared_results_name,
520521
inputs_size,
521522
results_size,
522523
n_sim_memory,
524+
export_sample_time,
523525
):
524526
"""
525527
Runs a single simulation worker.
@@ -528,6 +530,14 @@ def __run_simulation_worker(
528530
----------
529531
worker_no : int
530532
Worker number.
533+
n_workers : int
534+
Number of workers.
535+
light_mode : bool
536+
If True, only variables from the export_list will be saved to
537+
the output file as a .txt file. If False, all variables will be
538+
saved to the output file as a .h5 file.
539+
file_paths : dict
540+
Dictionary with the file paths.
531541
sto_env : StochasticEnvironment
532542
Stochastic environment object.
533543
sto_rocket : StochasticRocket
@@ -536,28 +546,26 @@ def __run_simulation_worker(
536546
Stochastic flight object.
537547
sim_counter : SimCounter
538548
Simulation counter object.
539-
inputs_lock : Lock
540-
Lock object for inputs file.
541-
outputs_lock : Lock
542-
Lock object for outputs file.
543549
errors_lock : Lock
544-
Lock object for errors file.
545-
buffer_lock : Lock
546-
Lock object for shared memory buffer.
547-
light_mode : bool
548-
If True, only variables from the export_list will be saved to
549-
the output file as a .txt file. If False, all variables will be
550-
saved to the output file as a .h5 file.
551-
file_paths : dict
552-
Dictionary with the file paths.
553-
shared_inputs_buffer : Array
554-
Shared memory buffer for inputs.
555-
shared_results_buffer : Array
556-
Shared memory buffer for results.
550+
Lock to write errors to the error file.
551+
go_write_inputs : list
552+
List of semaphores to write the inputs.
553+
go_write_results : list
554+
List of semaphores to write the results.
555+
go_read_inputs : list
556+
List of semaphores to read the inputs.
557+
go_read_results : list
558+
List of semaphores to read the results.
559+
shared_inputs_name : str
560+
Name of the shared memory buffer for the inputs.
561+
shared_results_name : str
562+
Name of the shared memory buffer for the results.
557563
inputs_size : int
558-
Size of the serialized dictionary.
564+
Size of the inputs to be written.
559565
results_size : int
560-
Size of the serialized dictionary.
566+
Size of the results to be written.
567+
n_sim_memory : int
568+
Number of simulations that can be stored in memory.
561569
562570
Returns
563571
-------
@@ -588,7 +596,7 @@ def __run_simulation_worker(
588596
initial_solution = sto_flight.initial_solution
589597
terminate_on_apogee = sto_flight.terminate_on_apogee
590598

591-
flight = Flight(
599+
monte_carlo_flight = Flight(
592600
rocket=rocket,
593601
environment=env,
594602
rail_length=rail_length,
@@ -609,48 +617,45 @@ def __run_simulation_worker(
609617
]
610618
for item in d.items()
611619
)
612-
613620
# Construct the dict with the results from the flight
614621
results = {
615-
export_item: getattr(flight, export_item)
622+
export_item: getattr(monte_carlo_flight, export_item)
616623
for export_item in file_paths["export_list"]
617624
}
618625

619-
export_inputs_ready = json.dumps(inputs_dict, cls=RocketPyEncoder)
620-
export_outputs_ready = json.dumps(results, cls=RocketPyEncoder)
626+
export_inputs = json.dumps(inputs_dict, cls=RocketPyEncoder)
627+
export_outputs = json.dumps(results, cls=RocketPyEncoder)
621628

622629
else:
623630
# serialize data
624-
input_parameters = flightv1_serializer(
625-
flight, f"Simulation_{sim_idx}", return_dict=True
631+
flight_results = MonteCarlo.inspect_object_attributes(
632+
monte_carlo_flight, sample_time=export_sample_time
626633
)
627634

628-
flight_results = MonteCarlo.inspect_object_attributes(flight)
629-
630-
# place in dictionary as it will be found in output file
635+
# place data in dictionary as it will be found in output file
631636
export_inputs = {
632-
str(sim_idx): input_parameters,
637+
str(
638+
sim_idx
639+
): "Currently no addition data is exported. Use the results file.",
633640
}
634641

635642
export_outputs = {
636643
str(sim_idx): flight_results,
637644
}
638645

639-
# downsample the arrays, ensuring the maximum size won't be exceeded
640-
export_inputs_ready = MonteCarlo.__downsample_recursive(
641-
data_dict=export_inputs,
642-
max_time=flight.max_time,
643-
sample_time=0.1,
644-
)
645-
export_outputs_ready = MonteCarlo.__downsample_recursive(
646-
data_dict=export_outputs,
647-
max_time=flight.max_time,
648-
sample_time=0.1,
646+
# convert to bytes
647+
export_inputs_bytes = pickle.dumps(export_inputs)
648+
export_outputs_bytes = pickle.dumps(export_outputs)
649+
650+
if len(export_inputs_bytes) > inputs_size:
651+
raise ValueError(
652+
"Input data is too large to fit in the shared memory buffer."
649653
)
650654

651-
# convert to bytes
652-
export_inputs_bytes = pickle.dumps(export_inputs_ready)
653-
export_outputs_bytes = pickle.dumps(export_outputs_ready)
655+
if len(export_outputs_bytes) > results_size:
656+
raise ValueError(
657+
"Output data is too large to fit in the shared memory buffer."
658+
)
654659

655660
# add padding to make sure the byte stream fits in the allocated space
656661
export_inputs_bytes = export_inputs_bytes.ljust(inputs_size, b'\0')
@@ -754,14 +759,21 @@ def __run_single_simulation(
754759
output_file=output_file,
755760
)
756761
else:
757-
input_parameters = flightv1_serializer(
758-
monte_carlo_flight, f"Simulation_{sim_idx}", return_dict=True
762+
# serialize data
763+
flight_results = MonteCarlo.inspect_object_attributes(
764+
monte_carlo_flight, sample_time=self.export_sample_time
759765
)
760766

761-
flight_results = self.inspect_object_attributes(monte_carlo_flight)
767+
# place data in dictionary as it will be found in output file
768+
export_inputs = {
769+
str(
770+
sim_idx
771+
): "Currently no addition data is exported. Use the results file.",
772+
}
762773

763-
export_inputs = {str(sim_idx): input_parameters}
764-
export_outputs = {str(sim_idx): flight_results}
774+
export_outputs = {
775+
str(sim_idx): flight_results,
776+
}
765777

766778
self.__dict_to_h5(input_file, '/', export_inputs)
767779
self.__dict_to_h5(output_file, '/', export_outputs)
@@ -913,7 +925,9 @@ def __get_export_size(self, light_mode):
913925
monte_carlo_flight = self.flight.create_object()
914926

915927
if monte_carlo_flight.max_time is None or monte_carlo_flight.max_time <= 0:
916-
raise ValueError("The max_time attribute must be greater than zero.")
928+
raise ValueError(
929+
"The max_time attribute must be greater than zero. To use parallel mode."
930+
)
917931

918932
# Export inputs and outputs to file
919933
if light_mode:
@@ -931,13 +945,13 @@ def __get_export_size(self, light_mode):
931945
for export_item in self.export_list
932946
}
933947
else:
934-
input_parameters = flightv1_serializer(
935-
monte_carlo_flight, f"probe_simulation", return_dict=True
948+
flight_results = self.inspect_object_attributes(
949+
monte_carlo_flight, self.export_sample_time
936950
)
937951

938-
flight_results = self.inspect_object_attributes(monte_carlo_flight)
939-
940-
export_inputs = {"probe_flight": input_parameters}
952+
export_inputs = {
953+
"probe_flight": "Currently no addition data is exported. Use the results file.",
954+
}
941955
results = {"probe_flight": flight_results}
942956

943957
# downsample the arrays, filling them up to the max time
@@ -1408,7 +1422,49 @@ def all_info(self):
14081422
self.plots.all()
14091423

14101424
@staticmethod
1411-
def inspect_object_attributes(obj):
1425+
def time_function_serializer(function_object, t_range=None, sample_time=None):
1426+
"""
1427+
Method to serialize a Function object into a numpy array. If the function is
1428+
callable, it will be discretized. If the downsample_time is specified, the
1429+
function will be downsampled. This serializer should not be used for
1430+
function that are not time dependent.
1431+
1432+
Parameters
1433+
----------
1434+
function_object : Function
1435+
Function object to be serialized.
1436+
t_range : tuple, optional
1437+
Tuple with the initial and final time of the function. Default is None.
1438+
sample_time : float, optional
1439+
Time interval between samples. Default is None.
1440+
1441+
Returns
1442+
-------
1443+
np.ndarray
1444+
Serialized function as a numpy array.
1445+
"""
1446+
func = deepcopy(function_object)
1447+
1448+
# Discretize the function if it is callable
1449+
if callable(function_object.source):
1450+
if t_range is not None:
1451+
func.set_discrete(*t_range, (t_range[1] - t_range[0]) / sample_time)
1452+
else:
1453+
raise ValueError("t_range must be specified for callable functions")
1454+
1455+
source = func.get_source()
1456+
1457+
# Ensure the downsampling is applied
1458+
if sample_time is not None:
1459+
t0 = source[0, 0]
1460+
tf = source[-1, 0]
1461+
t = np.arange(t0, tf, sample_time)
1462+
source = func(t)
1463+
1464+
return source
1465+
1466+
@staticmethod
1467+
def inspect_object_attributes(obj, sample_time=0.1):
14121468
"""
14131469
Inspects the attributes of an object and returns a dictionary of its
14141470
attributes.
@@ -1434,15 +1490,13 @@ def inspect_object_attributes(obj):
14341490

14351491
# Check if the attribute is of a type we are interested in and not a private attribute
14361492
if isinstance(
1437-
attr_value, (int, float, dict, Function)
1493+
attr_value, (int, float, Function)
14381494
) and not attr_name.startswith('_'):
14391495
if isinstance(attr_value, Function):
14401496
# Serialize the Functions
1441-
result[attr_name] = function_serializer(attr_value)
1442-
1443-
elif isinstance(attr_value, dict):
1444-
# Recursively inspect the dictionary attributes
1445-
result[attr_name] = MonteCarlo.inspect_object_attributes(attr_value)
1497+
result[attr_name] = MonteCarlo.time_function_serializer(
1498+
attr_value, None, sample_time
1499+
)
14461500

14471501
elif isinstance(attr_value, (int, float)):
14481502
result[attr_name] = attr_value

rocketpy/simulation/sim_config/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)