-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created tower class for use in TstMotionBasedCanting and associated t…
…ests
- Loading branch information
Showing
7 changed files
with
359 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
""" | ||
Tower Class | ||
Copyright (c) 2021 Sandia National Laboratories. | ||
""" | ||
|
||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
|
||
from opencsp.common.lib.geometry.Pxyz import Pxyz | ||
import opencsp.common.lib.render_control.RenderControlTower as rct | ||
from opencsp.common.lib.csp.RayTraceable import RayTraceable | ||
from opencsp.common.lib.render_control.RenderControlPointSeq import RenderControlPointSeq | ||
from opencsp.common.lib.render_control.RenderControlTower import RenderControlTower | ||
from opencsp.common.lib.render.View3d import View3d | ||
|
||
|
||
class Tower(RayTraceable): | ||
""" | ||
Tower representation. | ||
renders nsttf tower | ||
""" | ||
|
||
def __init__( | ||
self, | ||
name: str, | ||
origin: np.ndarray, | ||
parts: list[str] = ["whole tower"], | ||
height: float = 100, | ||
east: float = 8.8, | ||
west: float = -8.8, | ||
south: float = -8.8, | ||
north: float = 8.8, | ||
x_aim: float = 0, | ||
y_aim: float = 8.8, | ||
z_aim: float = 100, | ||
): | ||
|
||
# parameters used for control tower at NSTTF | ||
# tower_control= Tower(name='Sandia NSTTF Control Tower', | ||
# origin = np.array([0,0,0]), | ||
# height=25, | ||
# east = 8.8, | ||
# west = -8.8, | ||
# south = 284, | ||
# north = 300) | ||
"""Create a new Tower instance. | ||
Parameters: | ||
----------- | ||
name The name of this Tower. Used for special styles given in the draw method. | ||
origin The center of Tower, as a three vector xyz coordinate. | ||
all measurements in meters using ENU coordinate system. | ||
""" | ||
super(Tower, self).__init__() | ||
self.name = name | ||
self.origin = origin | ||
self.parts = parts | ||
self.height = height | ||
self.east = east | ||
self.west = west | ||
self.south = south | ||
self.north = north | ||
self.x_aim = x_aim | ||
self.y_aim = y_aim | ||
self.z_aim = z_aim | ||
self.target_loc = Pxyz([x_aim, y_aim, z_aim]) | ||
|
||
# Tower faces, top, and bottom | ||
self.top = [ | ||
[self.east, self.north, self.height], | ||
[self.west, self.north, self.height], | ||
[self.west, self.south, self.height], | ||
[self.east, self.south, self.height], | ||
] | ||
self.northface = [ | ||
[self.east, self.north, self.height], | ||
[self.west, self.north, self.height], | ||
[self.west, self.north, 0], | ||
[self.east, self.north, 0], | ||
] | ||
self.southface = [ | ||
[self.east, self.south, self.height], | ||
[self.west, self.south, self.height], | ||
[self.west, self.south, 0], | ||
[self.east, self.south, 0], | ||
] | ||
self.bottom = [ | ||
[self.east, self.north, 0], | ||
[self.west, self.north, 0], | ||
[self.west, self.south, 0], | ||
[self.east, self.south, 0], | ||
] | ||
|
||
self.point = [self.x_aim, self.y_aim, self.z_aim] | ||
|
||
# Centroid | ||
self.origin = np.array([origin[0], origin[1], origin[2]]) # Origin is at center of tower. | ||
|
||
def walls(self): | ||
"""Returns the list of walls in ul,ur,lr,ll order.""" | ||
# Assumes that Tower coordinates have been set, and the walls have been set. | ||
# # Later we can add a more meaningful check for this. | ||
return [self.top, self.northface, self.southface, self.bottom] | ||
|
||
# RENDERING | ||
|
||
def draw(self, view: View3d, tower_style: RenderControlTower) -> None: | ||
# Assumes that heliostat configuration has already been set. | ||
|
||
tower_style = tower_style.style(self.name) | ||
|
||
# Whole tower | ||
if 'whole tower' in self.parts: | ||
self.parts += ['top', 'northface', 'southface', 'northface', 'bottom'] | ||
|
||
# Top of tower | ||
if "top" in self.parts: | ||
view.draw_xyz_list(self.top, close=True, style=tower_style.wire_frame) | ||
|
||
# Northface of tower | ||
if "northface" in self.parts: | ||
view.draw_xyz_list(self.northface, close=True, style=tower_style.wire_frame) | ||
|
||
# target on northface of tower | ||
if "target" in self.parts: | ||
view.draw_xyz(self.point, style=tower_style.target) | ||
|
||
# Southface of tower | ||
if "southface" in self.parts: | ||
view.draw_xyz_list(self.southface, close=True, style=tower_style.wire_frame) | ||
|
||
# Bottom of tower | ||
if "bottom" in self.parts: | ||
view.draw_xyz_list(self.bottom, close=True, style=tower_style.wire_frame) | ||
|
||
return |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file added
BIN
+1.05 MB
opencsp/common/lib/test/data/input/TestTowerOutput/tto001_Single_Tower_3d.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions
16
opencsp/common/lib/test/data/input/TestTowerOutput/tto001_Single_Tower_3d.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Metadata: | ||
Figure number: 1 | ||
Name: tto001_Single Tower | ||
Title: Single Tower | ||
Code tag: TestTowerOutput.test_single_tower() | ||
View spec: 3d | ||
|
||
Title: | ||
Single Tower | ||
|
||
Caption: | ||
A single Sandia NSTTF tower. | ||
|
||
Comments: | ||
Demonstration of single 3d tower drawing. | ||
|
Binary file added
BIN
+1.19 MB
opencsp/common/lib/test/data/input/TestTowerOutput/tto002_Multiple_Towers_3d.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions
18
opencsp/common/lib/test/data/input/TestTowerOutput/tto002_Multiple_Towers_3d.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
Metadata: | ||
Figure number: 1 | ||
Name: tto002_Multiple Towers | ||
Title: Multiple Towers | ||
Code tag: TestTowerOutput.test_multiple_towers() | ||
View spec: 3d | ||
|
||
Title: | ||
Multiple Towers | ||
|
||
Caption: | ||
Sandia NSTTF reciever and control tower. | ||
|
||
Comments: | ||
Demonstration of single 3d tower drawing. | ||
Black tower is NSTTF receiver tower. | ||
Green tower is NSTTF control tower. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
""" | ||
Demonstrate Tower Plotting Routines | ||
Copyright (c) 2021 Sandia National Laboratories. | ||
""" | ||
|
||
from datetime import datetime | ||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
import os | ||
|
||
import opencsp.common.lib.csp.HeliostatConfiguration as hc | ||
from opencsp.common.lib.csp.SolarField import SolarField | ||
import opencsp.common.lib.csp.SolarField as sf | ||
import opencsp.common.lib.csp.sun_track as sun_track # "st" is taken by string_tools. | ||
from opencsp.common.lib.csp.Tower import Tower | ||
import opencsp.common.lib.opencsp_path.data_path_for_test as dpft | ||
import opencsp.common.lib.opencsp_path.opencsp_root_path as orp | ||
import opencsp.common.lib.render.figure_management as fm | ||
import opencsp.common.lib.render.view_spec as vs | ||
import opencsp.common.lib.render_control.RenderControlTower as rct | ||
import opencsp.common.lib.test.support_test as stest | ||
import opencsp.common.lib.test.TestOutput as to | ||
import opencsp.common.lib.tool.file_tools as ft | ||
import opencsp.common.lib.tool.log_tools as lt | ||
|
||
|
||
class TestTowerOutput(to.TestOutput): | ||
|
||
@classmethod | ||
def setUpClass( | ||
self, | ||
source_file_body: str = 'TestTowerOutput', # Set these here, because pytest calls | ||
figure_prefix_root: str = 'tto', # setup_class() with no arguments. | ||
interactive: bool = False, | ||
verify: bool = True, | ||
): | ||
|
||
# Save input. | ||
# Interactive mode flag. | ||
# This has two effects: | ||
# 1. If interactive mode, plots stay displayed. | ||
# 2. If interactive mode, after several plots have been displayed, | ||
# plot contents might change due to Matplotlib memory issues. | ||
# This may in turn cause misleading failures in comparing actual | ||
# output to expected output. | ||
# Thus: | ||
# - Run pytest in non-interactive mode. | ||
# - If viewing run output interactively, be advised that figure content might | ||
# change (e.g., garbled plot titles, etc), and actual-vs-expected comparison | ||
# might erroneously fail (meaning they might report an error which is not | ||
# actually a code error). | ||
# | ||
super(TestTowerOutput, self).setUpClass( | ||
source_file_body=source_file_body, | ||
figure_prefix_root=figure_prefix_root, | ||
interactive=interactive, | ||
verify=verify, | ||
) | ||
|
||
# Note: It is tempting to put the "Reset rendering" code lines here, to avoid redundant | ||
# computation and keep all the plots up. Don't do this, because the plotting system | ||
# will run low/out of memory, causing adverse effectes on the plots. | ||
|
||
def test_single_tower(self) -> None: | ||
""" | ||
Draws one tower. | ||
""" | ||
# Initialize test. | ||
self.start_test() | ||
|
||
# View setup | ||
title = 'Single Tower' | ||
caption = 'A single Sandia NSTTF tower.' | ||
comments = [] | ||
|
||
# Configuration setup | ||
tower = Tower(name='Sandia NSTTF', origin=np.array([0, 0, 0]), parts=["whole tower", "target"]) | ||
|
||
# Setup render control. | ||
# Style setup | ||
tower_control = rct.normal_tower() | ||
|
||
# comments\ | ||
comments.append("Demonstration of single 3d tower drawing.") | ||
|
||
# Draw. | ||
fig_record = fm.setup_figure_for_3d_data( | ||
self.figure_control, | ||
self.axis_control_m, | ||
vs.view_spec_3d(), | ||
number_in_name=False, | ||
input_prefix=self.figure_prefix( | ||
1 | ||
), # Figure numbers needed because titles may be identical. Hard-code number because test order is unpredictable. | ||
title=title, | ||
caption=caption, | ||
comments=comments, | ||
code_tag=self.code_tag, | ||
) | ||
tower.draw(fig_record.view, tower_control) | ||
|
||
# Output. | ||
self.show_save_and_check_figure(fig_record) | ||
|
||
def test_multiple_towers(self) -> None: | ||
""" | ||
Draws one tower. | ||
""" | ||
# Initialize test. | ||
self.start_test() | ||
|
||
# View setup | ||
title = 'Multiple Towers' | ||
caption = 'Sandia NSTTF reciever and control tower.' | ||
comments = [] | ||
|
||
# Configuration setup | ||
tower_receiver = Tower(name='Sandia NSTTF', origin=np.array([0, 0, 0]), parts=["whole tower", "target"]) | ||
tower_control = Tower( | ||
name='Sandia NSTTF Control Tower', | ||
origin=np.array([0, 0, 0]), | ||
height=50, | ||
east=8.8, | ||
west=-8.8, | ||
south=332.4, | ||
north=350, | ||
) | ||
|
||
# Setup render control. | ||
# Style setup | ||
tower_control_rec = rct.normal_tower() | ||
tower_control_con = rct.no_target() | ||
|
||
# comments\ | ||
comments.append("Demonstration of single 3d tower drawing.") | ||
comments.append("Black tower is NSTTF receiver tower.") | ||
comments.append("Green tower is NSTTF control tower.") | ||
|
||
# Draw. | ||
fig_record = fm.setup_figure_for_3d_data( | ||
self.figure_control, | ||
self.axis_control_m, | ||
vs.view_spec_3d(), | ||
number_in_name=False, | ||
input_prefix=self.figure_prefix( | ||
2 | ||
), # Figure numbers needed because titles may be identical. Hard-code number because test order is unpredictable. | ||
title=title, | ||
caption=caption, | ||
comments=comments, | ||
code_tag=self.code_tag, | ||
) | ||
tower_receiver.draw(fig_record.view, tower_control_rec) | ||
tower_control.draw(fig_record.view, tower_control_con) | ||
|
||
# Output. | ||
self.show_save_and_check_figure(fig_record) | ||
|
||
|
||
# MAIN EXECUTION | ||
|
||
if __name__ == "__main__": | ||
# Control flags. | ||
interactive = False | ||
# Set verify to False when you want to generate all figures and then copy | ||
# them into the expected_output directory. | ||
# (Does not affect pytest, which uses default value.) | ||
verify = True # False | ||
# Setup.py | ||
test_object = TestTowerOutput() | ||
test_object.setUpClass(interactive=interactive, verify=verify) | ||
# Tests. | ||
lt.info('Beginning tests...') | ||
test_object.test_single_tower() | ||
test_object.test_multiple_towers() | ||
|
||
lt.info('All tests complete.') | ||
# Cleanup. | ||
if interactive: | ||
input("Press Enter...") | ||
test_object.tearDown() |