diff --git a/cace/common/common.py b/cace/common/common.py index 32abbf1..94acab5 100644 --- a/cace/common/common.py +++ b/cace/common/common.py @@ -694,7 +694,8 @@ def run_subprocess( text=True, ) as process: - dbg(input) + if input != None: + dbg(f'input: {input}') stdout, stderr = process.communicate(input) returncode = process.returncode diff --git a/cace/parameter/parameter.py b/cace/parameter/parameter.py index b466e0c..1c57388 100755 --- a/cace/parameter/parameter.py +++ b/cace/parameter/parameter.py @@ -277,7 +277,7 @@ def get_result(self, name: str): return None def cancel(self, no_cb): - info(f'Parameter {self.pname}: Canceled') + info(f'Parameter {self.pname}: Canceled.') self.canceled = True if self.subproc_handle: @@ -366,7 +366,8 @@ def run_subprocess(self, proc, args=[], env=None, input=None, cwd=None): self.subproc_handle = process - dbg(input) + if input != None: + dbg(f'input: {input}') stdout, stderr = process.communicate(input) returncode = process.returncode @@ -497,14 +498,14 @@ def evaluate_result(self): # Scale value with unit if unit: - dbg(f'{unit} {value}') + dbg(f'scaling {value} with {unit}') value = spice_unit_convert( ( str(unit), str(value), ) ) - + dbg(f'result: {value}') if result != None: dbg( f'Checking result {result} against value {value} with limit {limit}.' @@ -893,7 +894,7 @@ def makeplot( not 'yaxis' in self.param['plot'][plot_name] and not 'xaxis' in self.param['plot'][plot_name] ): - err('Neither yaxis nor xaxis specified.') + err(f'Neither yaxis nor xaxis specified in plot {plot_name}.') xvariable = None if 'xaxis' in self.param['plot'][plot_name]: @@ -1181,7 +1182,7 @@ def makeplot( elif xvariable in conditions: xvalues = conditions[xvariable].values else: - err(f'Unknown variable: {xvariable}') + err(f'Unknown variable: {xvariable} in plot {plot_name}.') return None xvalues_list.append(xvalues) @@ -1196,7 +1197,9 @@ def makeplot( elif yvariable in condition_set: yvalues.append(condition_set[yvariable]) else: - err(f'Unknown variable: {yvariable}') + err( + f'Unknown variable: {yvariable} in plot {plot_name}.' + ) return None yvalues_list.append(yvalues) diff --git a/cace/parameter/parameter_ngspice.py b/cace/parameter/parameter_ngspice.py index 535a7db..b07b25f 100755 --- a/cace/parameter/parameter_ngspice.py +++ b/cace/parameter/parameter_ngspice.py @@ -62,7 +62,7 @@ def __init__( **kwargs, ) - self.add_argument(Argument('template', None, True)) + self.add_argument(Argument('template', None, True)) # TODO typing self.add_argument(Argument('collate', None, False)) self.add_argument(Argument('format', None, False)) self.add_argument(Argument('suffix', None, False)) @@ -153,7 +153,7 @@ def pre_start(self): def implementation(self): - info(f'Parameter {self.param["name"]}: Generating simulation files.') + info(f'Parameter {self.param["name"]}: Generating simulation files…') variables = self.get_argument('variables') @@ -236,10 +236,11 @@ def implementation(self): dbg(f'Total number of simulations: {self.num_sims}') # If "collate" is set this means we need to merge - # the results of this variable - # This is done by removing it from the conditions - # and + # the results were all conditions but the collate conditions is the same + # This is useful for MC simulations, where the results of different iterations, + # but under the same conditions (e.g. temperature) should be collated. + # First remove the collate condition from the conditions if self.get_argument('collate'): collate_variable = self.get_argument('collate') info(f'Collating results using condition "{collate_variable}"') @@ -249,7 +250,7 @@ def implementation(self): dbg(collate_condition) else: err( - f'Couldn\'t find collate variable "{collate_variable}" in conditions.' + f'Couldn\'t find condition "{collate_variable}" used for collating the results.' ) # Generate the condition sets for each simulation @@ -467,7 +468,7 @@ def implementation(self): # Run all simulations jobs = [] - info(f'Parameter {self.param["name"]}: Running simulations.') + info(f'Parameter {self.param["name"]}: Running simulations…') self.cancel_point() @@ -566,7 +567,7 @@ def implementation(self): self.cancel_point() - info(f'Parameter {self.param["name"]}: Collecting results.') + info(f'Parameter {self.param["name"]}: Collecting results…') # Get the result max_digits = len(str(len(condition_sets))) @@ -605,9 +606,6 @@ def implementation(self): ) # Read the result file - - collated_results = {} - if format == 'ascii': result_file = os.path.join( @@ -634,12 +632,18 @@ def implementation(self): collated_values[ variables[_index] ].append(float(entry)) - - dbg(f'collated_values: {collated_values}') - else: err(f'Unsupported format for the simulation result.') + dbg(f'collated values: {collated_values}') + + if self.get_argument('collate'): + condition_set[collate_variable] = collate_values + + dbg(f'collated condition: {condition_set[collate_variable]}') + + dbg(f'Extending final result…') + for variable in variables: if variable != None: # Extend the final result @@ -654,6 +658,10 @@ def implementation(self): self.datasheet['paths']['scripts'], script ) + info( + f"Running user-defined script '[repr.filename][link=file://{os.path.abspath(script_path)}]{os.path.relpath(script_path)}[/link][/repr.filename]'…" + ) + if not os.path.isfile(script_path): err(f'No such user script {script_path}.') self.result_type = ResultType.ERROR @@ -663,9 +671,33 @@ def implementation(self): user_script = SourceFileLoader( 'user_script', script_path ).load_module() - collated_values = user_script.postprocess( - collated_values, condition_set - ) + + class CustomPrint: + def __enter__(self): + self._stdout = sys.stdout + sys.stdout = self + return self + + def __exit__(self, *args): + sys.stdout = self._stdout + + def write(self, text): + text = text.rstrip() + if len(text) == 0: + return + info(f'User script: {text}') + + def flush(self): + self._stdout.flush() + + def __getattr__(self, attr): + return getattr(self._stdout, attr) + + with CustomPrint() as output: + collated_values = user_script.postprocess( + collated_values, condition_set + ) + except Exception as e: err(f'Error in user script: {e}') self.result_type = ResultType.ERROR diff --git a/default.nix b/default.nix index 692748b..bb3fa24 100644 --- a/default.nix +++ b/default.nix @@ -31,6 +31,7 @@ # Python matplotlib, numpy, + scipy, pillow, tkinter, rich, @@ -77,6 +78,7 @@ # Python matplotlib numpy + scipy pillow volare tkinter diff --git a/docs/source/index.md b/docs/source/index.md index 27c3cd7..2098ab8 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -24,7 +24,7 @@ Follow the navigation element below (or check the sidebar on the left) to get st getting_started/index usage/index reference_manual/index -tutorial/index +tutorials/index examples/index dev/index ``` diff --git a/docs/source/reference_manual/tools.md b/docs/source/reference_manual/tools.md index 34e702e..3f1e3b2 100644 --- a/docs/source/reference_manual/tools.md +++ b/docs/source/reference_manual/tools.md @@ -13,8 +13,10 @@ Arguments: - `format`: `<'ascii'>` The file format of the ngspice result. Currently only `ascii` is supported. - `suffix`: `` File extension of the result file. For example: `.data`. - `variables`: `` A list of results inside the result file. Use `null` to ignore a column. +- `script` (optional): `` Name of a Python script in the script folder. It will be executed on the results of each simulation. +- `script_variables` (optional): `` A list of results generated by the specified Python script. These results are available in addition to the ones specified under `variables`. TODO -Results: The results depend on the `variables` argument. +Results: The results depend on the `variables` and optionally the `script_variables` arguments. ## `magic_drc` diff --git a/docs/source/tutorials/custom_scripts.md b/docs/source/tutorials/custom_scripts.md new file mode 100644 index 0000000..144320a --- /dev/null +++ b/docs/source/tutorials/custom_scripts.md @@ -0,0 +1,2 @@ +# Tutorial - Using Custom Scripts to Preprocess Simulation Results + diff --git a/docs/source/tutorial/img/cli_1.png b/docs/source/tutorials/img/cli_1.png similarity index 100% rename from docs/source/tutorial/img/cli_1.png rename to docs/source/tutorials/img/cli_1.png diff --git a/docs/source/tutorial/img/cli_2.png b/docs/source/tutorials/img/cli_2.png similarity index 100% rename from docs/source/tutorial/img/cli_2.png rename to docs/source/tutorials/img/cli_2.png diff --git a/docs/source/tutorial/img/cli_3.png b/docs/source/tutorials/img/cli_3.png similarity index 100% rename from docs/source/tutorial/img/cli_3.png rename to docs/source/tutorials/img/cli_3.png diff --git a/docs/source/tutorial/img/gui_1.png b/docs/source/tutorials/img/gui_1.png similarity index 100% rename from docs/source/tutorial/img/gui_1.png rename to docs/source/tutorials/img/gui_1.png diff --git a/docs/source/tutorial/img/gui_2.png b/docs/source/tutorials/img/gui_2.png similarity index 100% rename from docs/source/tutorial/img/gui_2.png rename to docs/source/tutorials/img/gui_2.png diff --git a/docs/source/tutorial/img/gui_3.png b/docs/source/tutorials/img/gui_3.png similarity index 100% rename from docs/source/tutorial/img/gui_3.png rename to docs/source/tutorials/img/gui_3.png diff --git a/docs/source/tutorial/img/ota5t.png b/docs/source/tutorials/img/ota5t.png similarity index 100% rename from docs/source/tutorial/img/ota5t.png rename to docs/source/tutorials/img/ota5t.png diff --git a/docs/source/tutorial/img/ota5t_layout.png b/docs/source/tutorials/img/ota5t_layout.png similarity index 100% rename from docs/source/tutorial/img/ota5t_layout.png rename to docs/source/tutorials/img/ota5t_layout.png diff --git a/docs/source/tutorial/img/ota5t_sym.png b/docs/source/tutorials/img/ota5t_sym.png similarity index 100% rename from docs/source/tutorial/img/ota5t_sym.png rename to docs/source/tutorials/img/ota5t_sym.png diff --git a/docs/source/tutorial/img/ota5t_tb.png b/docs/source/tutorials/img/ota5t_tb.png similarity index 100% rename from docs/source/tutorial/img/ota5t_tb.png rename to docs/source/tutorials/img/ota5t_tb.png diff --git a/docs/source/tutorial/img/ota5t_tmp_tb.png b/docs/source/tutorials/img/ota5t_tmp_tb.png similarity index 100% rename from docs/source/tutorial/img/ota5t_tmp_tb.png rename to docs/source/tutorials/img/ota5t_tmp_tb.png diff --git a/docs/source/tutorials/index.md b/docs/source/tutorials/index.md new file mode 100644 index 0000000..f221a1b --- /dev/null +++ b/docs/source/tutorials/index.md @@ -0,0 +1,9 @@ +# Tutorials + +```{toctree} +:glob: +:maxdepth: 2 + +ota_5t +custom_scripts +``` diff --git a/docs/source/tutorial/index.md b/docs/source/tutorials/ota_5t.md similarity index 99% rename from docs/source/tutorial/index.md rename to docs/source/tutorials/ota_5t.md index 316e1c4..c54b685 100644 --- a/docs/source/tutorial/index.md +++ b/docs/source/tutorials/ota_5t.md @@ -1,4 +1,4 @@ -# Tutorial +# Tutorial - CACE Setup for a 5T-OTA This tutorial will guide you step by step through the creation of a simple 5-transistor OTA and the CACE setup for it. It is assumed that you have already set up CACE and its dependencies.